xref: /src/contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1145449b1SDimitry Andric //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
2145449b1SDimitry Andric //
3145449b1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4145449b1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5145449b1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6145449b1SDimitry Andric //
7145449b1SDimitry Andric //===----------------------------------------------------------------------===//
8145449b1SDimitry Andric ///
9145449b1SDimitry Andric /// \file
10e3b55780SDimitry Andric /// This file implements the ExtractAPIAction, and ASTConsumer to collect API
11e3b55780SDimitry Andric /// information.
12145449b1SDimitry Andric ///
13145449b1SDimitry Andric //===----------------------------------------------------------------------===//
14145449b1SDimitry Andric 
157fa27ce4SDimitry Andric #include "clang/AST/ASTConcept.h"
16145449b1SDimitry Andric #include "clang/AST/ASTConsumer.h"
17145449b1SDimitry Andric #include "clang/AST/ASTContext.h"
187fa27ce4SDimitry Andric #include "clang/AST/DeclObjC.h"
19e3b55780SDimitry Andric #include "clang/Basic/DiagnosticFrontend.h"
20b1c73532SDimitry Andric #include "clang/Basic/FileEntry.h"
21145449b1SDimitry Andric #include "clang/Basic/SourceLocation.h"
22145449b1SDimitry Andric #include "clang/Basic/SourceManager.h"
23145449b1SDimitry Andric #include "clang/Basic/TargetInfo.h"
24145449b1SDimitry Andric #include "clang/ExtractAPI/API.h"
25e3b55780SDimitry Andric #include "clang/ExtractAPI/APIIgnoresList.h"
26e3b55780SDimitry Andric #include "clang/ExtractAPI/ExtractAPIVisitor.h"
27145449b1SDimitry Andric #include "clang/ExtractAPI/FrontendActions.h"
28145449b1SDimitry Andric #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
29145449b1SDimitry Andric #include "clang/Frontend/ASTConsumers.h"
30145449b1SDimitry Andric #include "clang/Frontend/CompilerInstance.h"
31145449b1SDimitry Andric #include "clang/Frontend/FrontendOptions.h"
327fa27ce4SDimitry Andric #include "clang/Frontend/MultiplexConsumer.h"
33ac9a064cSDimitry Andric #include "clang/Index/USRGeneration.h"
34ac9a064cSDimitry Andric #include "clang/InstallAPI/HeaderFile.h"
35145449b1SDimitry Andric #include "clang/Lex/MacroInfo.h"
36145449b1SDimitry Andric #include "clang/Lex/PPCallbacks.h"
37145449b1SDimitry Andric #include "clang/Lex/Preprocessor.h"
38145449b1SDimitry Andric #include "clang/Lex/PreprocessorOptions.h"
39145449b1SDimitry Andric #include "llvm/ADT/DenseSet.h"
40145449b1SDimitry Andric #include "llvm/ADT/STLExtras.h"
417fa27ce4SDimitry Andric #include "llvm/ADT/SmallString.h"
42145449b1SDimitry Andric #include "llvm/ADT/SmallVector.h"
43ac9a064cSDimitry Andric #include "llvm/ADT/StringRef.h"
447fa27ce4SDimitry Andric #include "llvm/Support/Casting.h"
45e3b55780SDimitry Andric #include "llvm/Support/Error.h"
46145449b1SDimitry Andric #include "llvm/Support/FileSystem.h"
47145449b1SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
48145449b1SDimitry Andric #include "llvm/Support/Path.h"
49145449b1SDimitry Andric #include "llvm/Support/Regex.h"
50145449b1SDimitry Andric #include "llvm/Support/raw_ostream.h"
51145449b1SDimitry Andric #include <memory>
52e3b55780SDimitry Andric #include <optional>
53145449b1SDimitry Andric #include <utility>
54145449b1SDimitry Andric 
55145449b1SDimitry Andric using namespace clang;
56145449b1SDimitry Andric using namespace extractapi;
57145449b1SDimitry Andric 
58145449b1SDimitry Andric namespace {
59145449b1SDimitry Andric 
getRelativeIncludeName(const CompilerInstance & CI,StringRef File,bool * IsQuoted=nullptr)60e3b55780SDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
61145449b1SDimitry Andric                                                   StringRef File,
62145449b1SDimitry Andric                                                   bool *IsQuoted = nullptr) {
63145449b1SDimitry Andric   assert(CI.hasFileManager() &&
64145449b1SDimitry Andric          "CompilerInstance does not have a FileNamager!");
65145449b1SDimitry Andric 
66145449b1SDimitry Andric   using namespace llvm::sys;
67145449b1SDimitry Andric   const auto &FS = CI.getVirtualFileSystem();
68145449b1SDimitry Andric 
69145449b1SDimitry Andric   SmallString<128> FilePath(File.begin(), File.end());
70145449b1SDimitry Andric   FS.makeAbsolute(FilePath);
71145449b1SDimitry Andric   path::remove_dots(FilePath, true);
72145449b1SDimitry Andric   FilePath = path::convert_to_slash(FilePath);
73145449b1SDimitry Andric   File = FilePath;
74145449b1SDimitry Andric 
75145449b1SDimitry Andric   // Checks whether `Dir` is a strict path prefix of `File`. If so returns
76145449b1SDimitry Andric   // the prefix length. Otherwise return 0.
77145449b1SDimitry Andric   auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
78145449b1SDimitry Andric     llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
79145449b1SDimitry Andric     FS.makeAbsolute(DirPath);
80145449b1SDimitry Andric     path::remove_dots(DirPath, true);
81145449b1SDimitry Andric     Dir = DirPath;
82145449b1SDimitry Andric     for (auto NI = path::begin(File), NE = path::end(File),
83145449b1SDimitry Andric               DI = path::begin(Dir), DE = path::end(Dir);
84145449b1SDimitry Andric          /*termination condition in loop*/; ++NI, ++DI) {
85145449b1SDimitry Andric       // '.' components in File are ignored.
86145449b1SDimitry Andric       while (NI != NE && *NI == ".")
87145449b1SDimitry Andric         ++NI;
88145449b1SDimitry Andric       if (NI == NE)
89145449b1SDimitry Andric         break;
90145449b1SDimitry Andric 
91145449b1SDimitry Andric       // '.' components in Dir are ignored.
92145449b1SDimitry Andric       while (DI != DE && *DI == ".")
93145449b1SDimitry Andric         ++DI;
94145449b1SDimitry Andric 
95145449b1SDimitry Andric       // Dir is a prefix of File, up to '.' components and choice of path
96145449b1SDimitry Andric       // separators.
97145449b1SDimitry Andric       if (DI == DE)
98145449b1SDimitry Andric         return NI - path::begin(File);
99145449b1SDimitry Andric 
100145449b1SDimitry Andric       // Consider all path separators equal.
101145449b1SDimitry Andric       if (NI->size() == 1 && DI->size() == 1 &&
102145449b1SDimitry Andric           path::is_separator(NI->front()) && path::is_separator(DI->front()))
103145449b1SDimitry Andric         continue;
104145449b1SDimitry Andric 
105145449b1SDimitry Andric       // Special case Apple .sdk folders since the search path is typically a
106145449b1SDimitry Andric       // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
107145449b1SDimitry Andric       // located in `iPhoneSimulator.sdk` (the real folder).
108312c0ed1SDimitry Andric       if (NI->ends_with(".sdk") && DI->ends_with(".sdk")) {
109145449b1SDimitry Andric         StringRef NBasename = path::stem(*NI);
110145449b1SDimitry Andric         StringRef DBasename = path::stem(*DI);
111312c0ed1SDimitry Andric         if (DBasename.starts_with(NBasename))
112145449b1SDimitry Andric           continue;
113145449b1SDimitry Andric       }
114145449b1SDimitry Andric 
115145449b1SDimitry Andric       if (*NI != *DI)
116145449b1SDimitry Andric         break;
117145449b1SDimitry Andric     }
118145449b1SDimitry Andric     return 0;
119145449b1SDimitry Andric   };
120145449b1SDimitry Andric 
121145449b1SDimitry Andric   unsigned PrefixLength = 0;
122145449b1SDimitry Andric 
123145449b1SDimitry Andric   // Go through the search paths and find the first one that is a prefix of
124145449b1SDimitry Andric   // the header.
125145449b1SDimitry Andric   for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
126145449b1SDimitry Andric     // Note whether the match is found in a quoted entry.
127145449b1SDimitry Andric     if (IsQuoted)
128145449b1SDimitry Andric       *IsQuoted = Entry.Group == frontend::Quoted;
129145449b1SDimitry Andric 
130145449b1SDimitry Andric     if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
131145449b1SDimitry Andric       if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
132145449b1SDimitry Andric         // If this is a headermap entry, try to reverse lookup the full path
133145449b1SDimitry Andric         // for a spelled name before mapping.
134145449b1SDimitry Andric         StringRef SpelledFilename = HMap->reverseLookupFilename(File);
135145449b1SDimitry Andric         if (!SpelledFilename.empty())
136145449b1SDimitry Andric           return SpelledFilename.str();
137145449b1SDimitry Andric 
138145449b1SDimitry Andric         // No matching mapping in this headermap, try next search entry.
139145449b1SDimitry Andric         continue;
140145449b1SDimitry Andric       }
141145449b1SDimitry Andric     }
142145449b1SDimitry Andric 
143145449b1SDimitry Andric     // Entry is a directory search entry, try to check if it's a prefix of File.
144145449b1SDimitry Andric     PrefixLength = CheckDir(Entry.Path);
145145449b1SDimitry Andric     if (PrefixLength > 0) {
146145449b1SDimitry Andric       // The header is found in a framework path, construct the framework-style
147145449b1SDimitry Andric       // include name `<Framework/Header.h>`
148145449b1SDimitry Andric       if (Entry.IsFramework) {
149145449b1SDimitry Andric         SmallVector<StringRef, 4> Matches;
150ac9a064cSDimitry Andric         clang::installapi::HeaderFile::getFrameworkIncludeRule().match(
151ac9a064cSDimitry Andric             File, &Matches);
152145449b1SDimitry Andric         // Returned matches are always in stable order.
153145449b1SDimitry Andric         if (Matches.size() != 4)
154e3b55780SDimitry Andric           return std::nullopt;
155145449b1SDimitry Andric 
156145449b1SDimitry Andric         return path::convert_to_slash(
157145449b1SDimitry Andric             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
158145449b1SDimitry Andric              Matches[3])
159145449b1SDimitry Andric                 .str());
160145449b1SDimitry Andric       }
161145449b1SDimitry Andric 
162145449b1SDimitry Andric       // The header is found in a normal search path, strip the search path
163145449b1SDimitry Andric       // prefix to get an include name.
164145449b1SDimitry Andric       return path::convert_to_slash(File.drop_front(PrefixLength));
165145449b1SDimitry Andric     }
166145449b1SDimitry Andric   }
167145449b1SDimitry Andric 
168145449b1SDimitry Andric   // Couldn't determine a include name, use full path instead.
169e3b55780SDimitry Andric   return std::nullopt;
170145449b1SDimitry Andric }
171145449b1SDimitry Andric 
getRelativeIncludeName(const CompilerInstance & CI,FileEntryRef FE,bool * IsQuoted=nullptr)172b1c73532SDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
173b1c73532SDimitry Andric                                                   FileEntryRef FE,
174b1c73532SDimitry Andric                                                   bool *IsQuoted = nullptr) {
175b1c73532SDimitry Andric   return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted);
176b1c73532SDimitry Andric }
177b1c73532SDimitry Andric 
178145449b1SDimitry Andric struct LocationFileChecker {
operator ()__anon3cc241f40111::LocationFileChecker179e3b55780SDimitry Andric   bool operator()(SourceLocation Loc) {
180145449b1SDimitry Andric     // If the loc refers to a macro expansion we need to first get the file
181145449b1SDimitry Andric     // location of the expansion.
182145449b1SDimitry Andric     auto &SM = CI.getSourceManager();
183145449b1SDimitry Andric     auto FileLoc = SM.getFileLoc(Loc);
184145449b1SDimitry Andric     FileID FID = SM.getFileID(FileLoc);
185145449b1SDimitry Andric     if (FID.isInvalid())
186145449b1SDimitry Andric       return false;
187145449b1SDimitry Andric 
188b1c73532SDimitry Andric     OptionalFileEntryRef File = SM.getFileEntryRefForID(FID);
189145449b1SDimitry Andric     if (!File)
190145449b1SDimitry Andric       return false;
191145449b1SDimitry Andric 
192b1c73532SDimitry Andric     if (KnownFileEntries.count(*File))
193145449b1SDimitry Andric       return true;
194145449b1SDimitry Andric 
195b1c73532SDimitry Andric     if (ExternalFileEntries.count(*File))
196145449b1SDimitry Andric       return false;
197145449b1SDimitry Andric 
198145449b1SDimitry Andric     // Try to reduce the include name the same way we tried to include it.
199145449b1SDimitry Andric     bool IsQuoted = false;
200b1c73532SDimitry Andric     if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted))
201145449b1SDimitry Andric       if (llvm::any_of(KnownFiles,
202145449b1SDimitry Andric                        [&IsQuoted, &IncludeName](const auto &KnownFile) {
203145449b1SDimitry Andric                          return KnownFile.first.equals(*IncludeName) &&
204145449b1SDimitry Andric                                 KnownFile.second == IsQuoted;
205145449b1SDimitry Andric                        })) {
206b1c73532SDimitry Andric         KnownFileEntries.insert(*File);
207145449b1SDimitry Andric         return true;
208145449b1SDimitry Andric       }
209145449b1SDimitry Andric 
210145449b1SDimitry Andric     // Record that the file was not found to avoid future reverse lookup for
211145449b1SDimitry Andric     // the same file.
212b1c73532SDimitry Andric     ExternalFileEntries.insert(*File);
213145449b1SDimitry Andric     return false;
214145449b1SDimitry Andric   }
215145449b1SDimitry Andric 
LocationFileChecker__anon3cc241f40111::LocationFileChecker216145449b1SDimitry Andric   LocationFileChecker(const CompilerInstance &CI,
217145449b1SDimitry Andric                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
218145449b1SDimitry Andric       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
219145449b1SDimitry Andric     for (const auto &KnownFile : KnownFiles)
220145449b1SDimitry Andric       if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
221145449b1SDimitry Andric         KnownFileEntries.insert(*FileEntry);
222145449b1SDimitry Andric   }
223145449b1SDimitry Andric 
224145449b1SDimitry Andric private:
225145449b1SDimitry Andric   const CompilerInstance &CI;
226145449b1SDimitry Andric   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
227145449b1SDimitry Andric   llvm::DenseSet<const FileEntry *> KnownFileEntries;
228145449b1SDimitry Andric   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
229145449b1SDimitry Andric };
230145449b1SDimitry Andric 
2317fa27ce4SDimitry Andric struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
shouldDeclBeIncluded__anon3cc241f40111::BatchExtractAPIVisitor2327fa27ce4SDimitry Andric   bool shouldDeclBeIncluded(const Decl *D) const {
2337fa27ce4SDimitry Andric     bool ShouldBeIncluded = true;
2347fa27ce4SDimitry Andric     // Check that we have the definition for redeclarable types.
2357fa27ce4SDimitry Andric     if (auto *TD = llvm::dyn_cast<TagDecl>(D))
2367fa27ce4SDimitry Andric       ShouldBeIncluded = TD->isThisDeclarationADefinition();
2377fa27ce4SDimitry Andric     else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
2387fa27ce4SDimitry Andric       ShouldBeIncluded = Interface->isThisDeclarationADefinition();
2397fa27ce4SDimitry Andric     else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
2407fa27ce4SDimitry Andric       ShouldBeIncluded = Protocol->isThisDeclarationADefinition();
2417fa27ce4SDimitry Andric 
2427fa27ce4SDimitry Andric     ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation());
2437fa27ce4SDimitry Andric     return ShouldBeIncluded;
2447fa27ce4SDimitry Andric   }
2457fa27ce4SDimitry Andric 
BatchExtractAPIVisitor__anon3cc241f40111::BatchExtractAPIVisitor2467fa27ce4SDimitry Andric   BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context,
2477fa27ce4SDimitry Andric                          APISet &API)
2487fa27ce4SDimitry Andric       : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {}
2497fa27ce4SDimitry Andric 
2507fa27ce4SDimitry Andric private:
2517fa27ce4SDimitry Andric   LocationFileChecker &LCF;
2527fa27ce4SDimitry Andric };
2537fa27ce4SDimitry Andric 
2547fa27ce4SDimitry Andric class WrappingExtractAPIConsumer : public ASTConsumer {
255145449b1SDimitry Andric public:
WrappingExtractAPIConsumer(ASTContext & Context,APISet & API)2567fa27ce4SDimitry Andric   WrappingExtractAPIConsumer(ASTContext &Context, APISet &API)
2577fa27ce4SDimitry Andric       : Visitor(Context, API) {}
258145449b1SDimitry Andric 
HandleTranslationUnit(ASTContext & Context)259145449b1SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
260145449b1SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
261145449b1SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
262145449b1SDimitry Andric   }
263145449b1SDimitry Andric 
264145449b1SDimitry Andric private:
2657fa27ce4SDimitry Andric   ExtractAPIVisitor<> Visitor;
2667fa27ce4SDimitry Andric };
2677fa27ce4SDimitry Andric 
2687fa27ce4SDimitry Andric class ExtractAPIConsumer : public ASTConsumer {
2697fa27ce4SDimitry Andric public:
ExtractAPIConsumer(ASTContext & Context,std::unique_ptr<LocationFileChecker> LCF,APISet & API)2707fa27ce4SDimitry Andric   ExtractAPIConsumer(ASTContext &Context,
2717fa27ce4SDimitry Andric                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
2727fa27ce4SDimitry Andric       : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {}
2737fa27ce4SDimitry Andric 
HandleTranslationUnit(ASTContext & Context)2747fa27ce4SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
2757fa27ce4SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
2767fa27ce4SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
2777fa27ce4SDimitry Andric   }
2787fa27ce4SDimitry Andric 
2797fa27ce4SDimitry Andric private:
2807fa27ce4SDimitry Andric   BatchExtractAPIVisitor Visitor;
281145449b1SDimitry Andric   std::unique_ptr<LocationFileChecker> LCF;
282145449b1SDimitry Andric };
283145449b1SDimitry Andric 
284145449b1SDimitry Andric class MacroCallback : public PPCallbacks {
285145449b1SDimitry Andric public:
MacroCallback(const SourceManager & SM,APISet & API,Preprocessor & PP)2867fa27ce4SDimitry Andric   MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP)
2877fa27ce4SDimitry Andric       : SM(SM), API(API), PP(PP) {}
288145449b1SDimitry Andric 
MacroDefined(const Token & MacroNameToken,const MacroDirective * MD)289145449b1SDimitry Andric   void MacroDefined(const Token &MacroNameToken,
290145449b1SDimitry Andric                     const MacroDirective *MD) override {
291145449b1SDimitry Andric     auto *MacroInfo = MD->getMacroInfo();
292145449b1SDimitry Andric 
293145449b1SDimitry Andric     if (MacroInfo->isBuiltinMacro())
294145449b1SDimitry Andric       return;
295145449b1SDimitry Andric 
296145449b1SDimitry Andric     auto SourceLoc = MacroNameToken.getLocation();
297145449b1SDimitry Andric     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
298145449b1SDimitry Andric         SM.isWrittenInCommandLineFile(SourceLoc))
299145449b1SDimitry Andric       return;
300145449b1SDimitry Andric 
301145449b1SDimitry Andric     PendingMacros.emplace_back(MacroNameToken, MD);
302145449b1SDimitry Andric   }
303145449b1SDimitry Andric 
304145449b1SDimitry Andric   // If a macro gets undefined at some point during preprocessing of the inputs
305145449b1SDimitry Andric   // it means that it isn't an exposed API and we should therefore not add a
306145449b1SDimitry Andric   // macro definition for it.
MacroUndefined(const Token & MacroNameToken,const MacroDefinition & MD,const MacroDirective * Undef)307145449b1SDimitry Andric   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
308145449b1SDimitry Andric                       const MacroDirective *Undef) override {
309145449b1SDimitry Andric     // If this macro wasn't previously defined we don't need to do anything
310145449b1SDimitry Andric     // here.
311145449b1SDimitry Andric     if (!Undef)
312145449b1SDimitry Andric       return;
313145449b1SDimitry Andric 
314145449b1SDimitry Andric     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
315145449b1SDimitry Andric       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
316145449b1SDimitry Andric                                               /*Syntactically*/ false);
317145449b1SDimitry Andric     });
318145449b1SDimitry Andric   }
319145449b1SDimitry Andric 
EndOfMainFile()320145449b1SDimitry Andric   void EndOfMainFile() override {
321145449b1SDimitry Andric     for (auto &PM : PendingMacros) {
322145449b1SDimitry Andric       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
323145449b1SDimitry Andric       // file so check for it here.
324145449b1SDimitry Andric       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
325145449b1SDimitry Andric         continue;
326145449b1SDimitry Andric 
3277fa27ce4SDimitry Andric       if (!shouldMacroBeIncluded(PM))
328145449b1SDimitry Andric         continue;
329145449b1SDimitry Andric 
330145449b1SDimitry Andric       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
331145449b1SDimitry Andric       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
332ac9a064cSDimitry Andric       SmallString<128> USR;
333ac9a064cSDimitry Andric       index::generateUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM,
334ac9a064cSDimitry Andric                                  USR);
335145449b1SDimitry Andric 
336ac9a064cSDimitry Andric       API.createRecord<extractapi::MacroDefinitionRecord>(
337ac9a064cSDimitry Andric           USR, Name, SymbolReference(), Loc,
338145449b1SDimitry Andric           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
339e3b55780SDimitry Andric           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
340e3b55780SDimitry Andric           SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
341145449b1SDimitry Andric     }
342145449b1SDimitry Andric 
343145449b1SDimitry Andric     PendingMacros.clear();
344145449b1SDimitry Andric   }
345145449b1SDimitry Andric 
3467fa27ce4SDimitry Andric protected:
347145449b1SDimitry Andric   struct PendingMacro {
348145449b1SDimitry Andric     Token MacroNameToken;
349145449b1SDimitry Andric     const MacroDirective *MD;
350145449b1SDimitry Andric 
PendingMacro__anon3cc241f40111::MacroCallback::PendingMacro351145449b1SDimitry Andric     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
352145449b1SDimitry Andric         : MacroNameToken(MacroNameToken), MD(MD) {}
353145449b1SDimitry Andric   };
354145449b1SDimitry Andric 
shouldMacroBeIncluded(const PendingMacro & PM)3557fa27ce4SDimitry Andric   virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; }
3567fa27ce4SDimitry Andric 
357145449b1SDimitry Andric   const SourceManager &SM;
358145449b1SDimitry Andric   APISet &API;
359145449b1SDimitry Andric   Preprocessor &PP;
360145449b1SDimitry Andric   llvm::SmallVector<PendingMacro> PendingMacros;
361145449b1SDimitry Andric };
362145449b1SDimitry Andric 
3637fa27ce4SDimitry Andric class APIMacroCallback : public MacroCallback {
3647fa27ce4SDimitry Andric public:
APIMacroCallback(const SourceManager & SM,APISet & API,Preprocessor & PP,LocationFileChecker & LCF)3657fa27ce4SDimitry Andric   APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP,
3667fa27ce4SDimitry Andric                    LocationFileChecker &LCF)
3677fa27ce4SDimitry Andric       : MacroCallback(SM, API, PP), LCF(LCF) {}
3687fa27ce4SDimitry Andric 
shouldMacroBeIncluded(const PendingMacro & PM)3697fa27ce4SDimitry Andric   bool shouldMacroBeIncluded(const PendingMacro &PM) override {
3707fa27ce4SDimitry Andric     // Do not include macros from external files
3717fa27ce4SDimitry Andric     return LCF(PM.MacroNameToken.getLocation());
3727fa27ce4SDimitry Andric   }
3737fa27ce4SDimitry Andric 
3747fa27ce4SDimitry Andric private:
3757fa27ce4SDimitry Andric   LocationFileChecker &LCF;
3767fa27ce4SDimitry Andric };
3777fa27ce4SDimitry Andric 
378ac9a064cSDimitry Andric std::unique_ptr<llvm::raw_pwrite_stream>
createAdditionalSymbolGraphFile(CompilerInstance & CI,Twine BaseName)379ac9a064cSDimitry Andric createAdditionalSymbolGraphFile(CompilerInstance &CI, Twine BaseName) {
380ac9a064cSDimitry Andric   auto OutputDirectory = CI.getFrontendOpts().SymbolGraphOutputDir;
381145449b1SDimitry Andric 
382ac9a064cSDimitry Andric   SmallString<256> FileName;
383ac9a064cSDimitry Andric   llvm::sys::path::append(FileName, OutputDirectory,
384ac9a064cSDimitry Andric                           BaseName + ".symbols.json");
385ac9a064cSDimitry Andric   return CI.createOutputFile(
386ac9a064cSDimitry Andric       FileName, /*Binary*/ false, /*RemoveFileOnSignal*/ false,
387ac9a064cSDimitry Andric       /*UseTemporary*/ true, /*CreateMissingDirectories*/ true);
3887fa27ce4SDimitry Andric }
3897fa27ce4SDimitry Andric 
390ac9a064cSDimitry Andric } // namespace
391ac9a064cSDimitry Andric 
ImplEndSourceFileAction(CompilerInstance & CI)392ac9a064cSDimitry Andric void ExtractAPIActionBase::ImplEndSourceFileAction(CompilerInstance &CI) {
393ac9a064cSDimitry Andric   SymbolGraphSerializerOption SerializationOptions;
394ac9a064cSDimitry Andric   SerializationOptions.Compact = !CI.getFrontendOpts().EmitPrettySymbolGraphs;
395ac9a064cSDimitry Andric   SerializationOptions.EmitSymbolLabelsForTesting =
396ac9a064cSDimitry Andric       CI.getFrontendOpts().EmitSymbolGraphSymbolLabelsForTesting;
397ac9a064cSDimitry Andric 
398ac9a064cSDimitry Andric   if (CI.getFrontendOpts().EmitExtensionSymbolGraphs) {
399ac9a064cSDimitry Andric     auto ConstructOutputFile = [&CI](Twine BaseName) {
400ac9a064cSDimitry Andric       return createAdditionalSymbolGraphFile(CI, BaseName);
401ac9a064cSDimitry Andric     };
402ac9a064cSDimitry Andric 
403ac9a064cSDimitry Andric     SymbolGraphSerializer::serializeWithExtensionGraphs(
404ac9a064cSDimitry Andric         *OS, *API, IgnoresList, ConstructOutputFile, SerializationOptions);
405ac9a064cSDimitry Andric   } else {
406ac9a064cSDimitry Andric     SymbolGraphSerializer::serializeMainSymbolGraph(*OS, *API, IgnoresList,
407ac9a064cSDimitry Andric                                                     SerializationOptions);
408ac9a064cSDimitry Andric   }
409ac9a064cSDimitry Andric 
410ac9a064cSDimitry Andric   // Flush the stream and close the main output stream.
411ac9a064cSDimitry Andric   OS.reset();
4127fa27ce4SDimitry Andric }
4137fa27ce4SDimitry Andric 
414145449b1SDimitry Andric std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)415145449b1SDimitry Andric ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
416ac9a064cSDimitry Andric   auto ProductName = CI.getFrontendOpts().ProductName;
417ac9a064cSDimitry Andric 
418ac9a064cSDimitry Andric   if (CI.getFrontendOpts().SymbolGraphOutputDir.empty())
419ac9a064cSDimitry Andric     OS = CI.createDefaultOutputFile(/*Binary*/ false, InFile,
420ac9a064cSDimitry Andric                                     /*Extension*/ "symbols.json",
421ac9a064cSDimitry Andric                                     /*RemoveFileOnSignal*/ false,
422ac9a064cSDimitry Andric                                     /*CreateMissingDirectories*/ true);
423ac9a064cSDimitry Andric   else
424ac9a064cSDimitry Andric     OS = createAdditionalSymbolGraphFile(CI, ProductName);
4257fa27ce4SDimitry Andric 
426145449b1SDimitry Andric   if (!OS)
427145449b1SDimitry Andric     return nullptr;
428145449b1SDimitry Andric 
429145449b1SDimitry Andric   // Now that we have enough information about the language options and the
430145449b1SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
431145449b1SDimitry Andric   API = std::make_unique<APISet>(
432145449b1SDimitry Andric       CI.getTarget().getTriple(),
433e3b55780SDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
434145449b1SDimitry Andric 
435145449b1SDimitry Andric   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
436145449b1SDimitry Andric 
4377fa27ce4SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>(
4387fa27ce4SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF));
439145449b1SDimitry Andric 
440e3b55780SDimitry Andric   // Do not include location in anonymous decls.
441e3b55780SDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
442e3b55780SDimitry Andric   Policy.AnonymousTagLocations = false;
443e3b55780SDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
444e3b55780SDimitry Andric 
4457fa27ce4SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
446e3b55780SDimitry Andric     llvm::handleAllErrors(
4477fa27ce4SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
448e3b55780SDimitry Andric                                CI.getFileManager())
449e3b55780SDimitry Andric             .moveInto(IgnoresList),
450e3b55780SDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
451e3b55780SDimitry Andric           CI.getDiagnostics().Report(
452e3b55780SDimitry Andric               diag::err_extract_api_ignores_file_not_found)
453e3b55780SDimitry Andric               << Err.Path;
454e3b55780SDimitry Andric         });
455e3b55780SDimitry Andric   }
456e3b55780SDimitry Andric 
457145449b1SDimitry Andric   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
458145449b1SDimitry Andric                                               std::move(LCF), *API);
459145449b1SDimitry Andric }
460145449b1SDimitry Andric 
PrepareToExecuteAction(CompilerInstance & CI)461145449b1SDimitry Andric bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
462145449b1SDimitry Andric   auto &Inputs = CI.getFrontendOpts().Inputs;
463145449b1SDimitry Andric   if (Inputs.empty())
464145449b1SDimitry Andric     return true;
465145449b1SDimitry Andric 
466145449b1SDimitry Andric   if (!CI.hasFileManager())
467145449b1SDimitry Andric     if (!CI.createFileManager())
468145449b1SDimitry Andric       return false;
469145449b1SDimitry Andric 
470145449b1SDimitry Andric   auto Kind = Inputs[0].getKind();
471145449b1SDimitry Andric 
472145449b1SDimitry Andric   // Convert the header file inputs into a single input buffer.
473145449b1SDimitry Andric   SmallString<256> HeaderContents;
474145449b1SDimitry Andric   bool IsQuoted = false;
475145449b1SDimitry Andric   for (const FrontendInputFile &FIF : Inputs) {
476145449b1SDimitry Andric     if (Kind.isObjectiveC())
477145449b1SDimitry Andric       HeaderContents += "#import";
478145449b1SDimitry Andric     else
479145449b1SDimitry Andric       HeaderContents += "#include";
480145449b1SDimitry Andric 
481145449b1SDimitry Andric     StringRef FilePath = FIF.getFile();
482145449b1SDimitry Andric     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
483145449b1SDimitry Andric       if (IsQuoted)
484145449b1SDimitry Andric         HeaderContents += " \"";
485145449b1SDimitry Andric       else
486145449b1SDimitry Andric         HeaderContents += " <";
487145449b1SDimitry Andric 
488145449b1SDimitry Andric       HeaderContents += *RelativeName;
489145449b1SDimitry Andric 
490145449b1SDimitry Andric       if (IsQuoted)
491145449b1SDimitry Andric         HeaderContents += "\"\n";
492145449b1SDimitry Andric       else
493145449b1SDimitry Andric         HeaderContents += ">\n";
494145449b1SDimitry Andric       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
495145449b1SDimitry Andric                                    IsQuoted);
496145449b1SDimitry Andric     } else {
497145449b1SDimitry Andric       HeaderContents += " \"";
498145449b1SDimitry Andric       HeaderContents += FilePath;
499145449b1SDimitry Andric       HeaderContents += "\"\n";
500145449b1SDimitry Andric       KnownInputFiles.emplace_back(FilePath, true);
501145449b1SDimitry Andric     }
502145449b1SDimitry Andric   }
503145449b1SDimitry Andric 
504145449b1SDimitry Andric   if (CI.getHeaderSearchOpts().Verbose)
505145449b1SDimitry Andric     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
506145449b1SDimitry Andric                                 << HeaderContents << "\n";
507145449b1SDimitry Andric 
508145449b1SDimitry Andric   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
509145449b1SDimitry Andric                                                 getInputBufferName());
510145449b1SDimitry Andric 
511145449b1SDimitry Andric   // Set that buffer up as our "real" input in the CompilerInstance.
512145449b1SDimitry Andric   Inputs.clear();
513145449b1SDimitry Andric   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
514145449b1SDimitry Andric 
515145449b1SDimitry Andric   return true;
516145449b1SDimitry Andric }
517145449b1SDimitry Andric 
EndSourceFileAction()518ac9a064cSDimitry Andric void ExtractAPIAction::EndSourceFileAction() {
519ac9a064cSDimitry Andric   ImplEndSourceFileAction(getCompilerInstance());
520ac9a064cSDimitry Andric }
521145449b1SDimitry Andric 
5227fa27ce4SDimitry Andric std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)5237fa27ce4SDimitry Andric WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
5247fa27ce4SDimitry Andric                                             StringRef InFile) {
5257fa27ce4SDimitry Andric   auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
5267fa27ce4SDimitry Andric   if (!OtherConsumer)
5277fa27ce4SDimitry Andric     return nullptr;
5287fa27ce4SDimitry Andric 
5297fa27ce4SDimitry Andric   CreatedASTConsumer = true;
5307fa27ce4SDimitry Andric 
531ac9a064cSDimitry Andric   ProductName = CI.getFrontendOpts().ProductName;
532ac9a064cSDimitry Andric   auto InputFilename = llvm::sys::path::filename(InFile);
533ac9a064cSDimitry Andric   OS = createAdditionalSymbolGraphFile(CI, InputFilename);
5347fa27ce4SDimitry Andric 
5357fa27ce4SDimitry Andric   // Now that we have enough information about the language options and the
5367fa27ce4SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
5377fa27ce4SDimitry Andric   API = std::make_unique<APISet>(
5387fa27ce4SDimitry Andric       CI.getTarget().getTriple(),
5397fa27ce4SDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
5407fa27ce4SDimitry Andric 
5417fa27ce4SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
5427fa27ce4SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor()));
5437fa27ce4SDimitry Andric 
5447fa27ce4SDimitry Andric   // Do not include location in anonymous decls.
5457fa27ce4SDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
5467fa27ce4SDimitry Andric   Policy.AnonymousTagLocations = false;
5477fa27ce4SDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
5487fa27ce4SDimitry Andric 
5497fa27ce4SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
5507fa27ce4SDimitry Andric     llvm::handleAllErrors(
5517fa27ce4SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
5527fa27ce4SDimitry Andric                                CI.getFileManager())
5537fa27ce4SDimitry Andric             .moveInto(IgnoresList),
5547fa27ce4SDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
5557fa27ce4SDimitry Andric           CI.getDiagnostics().Report(
5567fa27ce4SDimitry Andric               diag::err_extract_api_ignores_file_not_found)
5577fa27ce4SDimitry Andric               << Err.Path;
5587fa27ce4SDimitry Andric         });
5597fa27ce4SDimitry Andric   }
5607fa27ce4SDimitry Andric 
5617fa27ce4SDimitry Andric   auto WrappingConsumer =
5627fa27ce4SDimitry Andric       std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API);
5637fa27ce4SDimitry Andric   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
5647fa27ce4SDimitry Andric   Consumers.push_back(std::move(OtherConsumer));
5657fa27ce4SDimitry Andric   Consumers.push_back(std::move(WrappingConsumer));
5667fa27ce4SDimitry Andric 
5677fa27ce4SDimitry Andric   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
5687fa27ce4SDimitry Andric }
5697fa27ce4SDimitry Andric 
EndSourceFileAction()5707fa27ce4SDimitry Andric void WrappingExtractAPIAction::EndSourceFileAction() {
5717fa27ce4SDimitry Andric   // Invoke wrapped action's method.
5727fa27ce4SDimitry Andric   WrapperFrontendAction::EndSourceFileAction();
5737fa27ce4SDimitry Andric 
5747fa27ce4SDimitry Andric   if (CreatedASTConsumer) {
575ac9a064cSDimitry Andric     ImplEndSourceFileAction(getCompilerInstance());
5767fa27ce4SDimitry Andric   }
577145449b1SDimitry Andric }
578