xref: /llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (revision b1aea98cfa357e23f4bb52232da5f41781f23bff)
189f6b26fSZixu Wang //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
289f6b26fSZixu Wang //
389f6b26fSZixu Wang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
489f6b26fSZixu Wang // See https://llvm.org/LICENSE.txt for license information.
589f6b26fSZixu Wang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
689f6b26fSZixu Wang //
789f6b26fSZixu Wang //===----------------------------------------------------------------------===//
889f6b26fSZixu Wang ///
989f6b26fSZixu Wang /// \file
107a851921SDaniel Grumberg /// This file implements the ExtractAPIAction, and ASTConsumer to collect API
117a851921SDaniel Grumberg /// information.
1289f6b26fSZixu Wang ///
1389f6b26fSZixu Wang //===----------------------------------------------------------------------===//
1489f6b26fSZixu Wang 
15142c3d9dSDaniel Grumberg #include "clang/AST/ASTConcept.h"
1689f6b26fSZixu Wang #include "clang/AST/ASTConsumer.h"
1789f6b26fSZixu Wang #include "clang/AST/ASTContext.h"
18142c3d9dSDaniel Grumberg #include "clang/AST/DeclObjC.h"
19791fe26dSDaniel Grumberg #include "clang/Basic/DiagnosticFrontend.h"
2014e99174SDaniel Grumberg #include "clang/Basic/FileEntry.h"
21aebe5fc6SDaniel Grumberg #include "clang/Basic/SourceLocation.h"
22aebe5fc6SDaniel Grumberg #include "clang/Basic/SourceManager.h"
2389f6b26fSZixu Wang #include "clang/Basic/TargetInfo.h"
2489f6b26fSZixu Wang #include "clang/ExtractAPI/API.h"
25791fe26dSDaniel Grumberg #include "clang/ExtractAPI/APIIgnoresList.h"
267a851921SDaniel Grumberg #include "clang/ExtractAPI/ExtractAPIVisitor.h"
2789f6b26fSZixu Wang #include "clang/ExtractAPI/FrontendActions.h"
2889f6b26fSZixu Wang #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
2989f6b26fSZixu Wang #include "clang/Frontend/ASTConsumers.h"
3089f6b26fSZixu Wang #include "clang/Frontend/CompilerInstance.h"
31f833aab0SDaniel Grumberg #include "clang/Frontend/FrontendOptions.h"
328e9145e4SAnkur #include "clang/Frontend/MultiplexConsumer.h"
33e05c1b46SDaniel Grumberg #include "clang/Index/USRGeneration.h"
344c6043deSCyndy Ishida #include "clang/InstallAPI/HeaderFile.h"
35529a0570SDaniel Grumberg #include "clang/Lex/MacroInfo.h"
36529a0570SDaniel Grumberg #include "clang/Lex/PPCallbacks.h"
37aebe5fc6SDaniel Grumberg #include "clang/Lex/Preprocessor.h"
38529a0570SDaniel Grumberg #include "clang/Lex/PreprocessorOptions.h"
39aebe5fc6SDaniel Grumberg #include "llvm/ADT/DenseSet.h"
40529a0570SDaniel Grumberg #include "llvm/ADT/STLExtras.h"
418e9145e4SAnkur #include "llvm/ADT/SmallString.h"
42f833aab0SDaniel Grumberg #include "llvm/ADT/SmallVector.h"
43e05c1b46SDaniel Grumberg #include "llvm/ADT/StringRef.h"
44142c3d9dSDaniel Grumberg #include "llvm/Support/Casting.h"
45791fe26dSDaniel Grumberg #include "llvm/Support/Error.h"
46cb5bb285SZixu Wang #include "llvm/Support/FileSystem.h"
47f833aab0SDaniel Grumberg #include "llvm/Support/MemoryBuffer.h"
48cb5bb285SZixu Wang #include "llvm/Support/Path.h"
49cb5bb285SZixu Wang #include "llvm/Support/Regex.h"
5089f6b26fSZixu Wang #include "llvm/Support/raw_ostream.h"
51aebe5fc6SDaniel Grumberg #include <memory>
52f655cb0fSKazu Hirata #include <optional>
53aebe5fc6SDaniel Grumberg #include <utility>
5489f6b26fSZixu Wang 
5589f6b26fSZixu Wang using namespace clang;
5689f6b26fSZixu Wang using namespace extractapi;
5789f6b26fSZixu Wang 
5889f6b26fSZixu Wang namespace {
5989f6b26fSZixu Wang 
606ad0788cSKazu Hirata std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
61cb5bb285SZixu Wang                                                   StringRef File,
62cb5bb285SZixu Wang                                                   bool *IsQuoted = nullptr) {
63cb5bb285SZixu Wang   assert(CI.hasFileManager() &&
64cb5bb285SZixu Wang          "CompilerInstance does not have a FileNamager!");
65cb5bb285SZixu Wang 
66cb5bb285SZixu Wang   using namespace llvm::sys;
67cb5bb285SZixu Wang   const auto &FS = CI.getVirtualFileSystem();
68cb5bb285SZixu Wang 
69cb5bb285SZixu Wang   SmallString<128> FilePath(File.begin(), File.end());
70cb5bb285SZixu Wang   FS.makeAbsolute(FilePath);
71cb5bb285SZixu Wang   path::remove_dots(FilePath, true);
72cb5bb285SZixu Wang   FilePath = path::convert_to_slash(FilePath);
73cb5bb285SZixu Wang   File = FilePath;
74cb5bb285SZixu Wang 
75cb5bb285SZixu Wang   // Checks whether `Dir` is a strict path prefix of `File`. If so returns
76cb5bb285SZixu Wang   // the prefix length. Otherwise return 0.
77cb5bb285SZixu Wang   auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
78cb5bb285SZixu Wang     llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
79cb5bb285SZixu Wang     FS.makeAbsolute(DirPath);
80cb5bb285SZixu Wang     path::remove_dots(DirPath, true);
81cb5bb285SZixu Wang     Dir = DirPath;
82cb5bb285SZixu Wang     for (auto NI = path::begin(File), NE = path::end(File),
83cb5bb285SZixu Wang               DI = path::begin(Dir), DE = path::end(Dir);
84cb5bb285SZixu Wang          /*termination condition in loop*/; ++NI, ++DI) {
85cb5bb285SZixu Wang       // '.' components in File are ignored.
86cb5bb285SZixu Wang       while (NI != NE && *NI == ".")
87cb5bb285SZixu Wang         ++NI;
88cb5bb285SZixu Wang       if (NI == NE)
89cb5bb285SZixu Wang         break;
90cb5bb285SZixu Wang 
91cb5bb285SZixu Wang       // '.' components in Dir are ignored.
92cb5bb285SZixu Wang       while (DI != DE && *DI == ".")
93cb5bb285SZixu Wang         ++DI;
94cb5bb285SZixu Wang 
95cb5bb285SZixu Wang       // Dir is a prefix of File, up to '.' components and choice of path
96cb5bb285SZixu Wang       // separators.
97cb5bb285SZixu Wang       if (DI == DE)
98cb5bb285SZixu Wang         return NI - path::begin(File);
99cb5bb285SZixu Wang 
100cb5bb285SZixu Wang       // Consider all path separators equal.
101cb5bb285SZixu Wang       if (NI->size() == 1 && DI->size() == 1 &&
102cb5bb285SZixu Wang           path::is_separator(NI->front()) && path::is_separator(DI->front()))
103cb5bb285SZixu Wang         continue;
104cb5bb285SZixu Wang 
105cb5bb285SZixu Wang       // Special case Apple .sdk folders since the search path is typically a
106cb5bb285SZixu Wang       // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
107cb5bb285SZixu Wang       // located in `iPhoneSimulator.sdk` (the real folder).
108f3dcc235SKazu Hirata       if (NI->ends_with(".sdk") && DI->ends_with(".sdk")) {
109cb5bb285SZixu Wang         StringRef NBasename = path::stem(*NI);
110cb5bb285SZixu Wang         StringRef DBasename = path::stem(*DI);
111f3dcc235SKazu Hirata         if (DBasename.starts_with(NBasename))
112cb5bb285SZixu Wang           continue;
113cb5bb285SZixu Wang       }
114cb5bb285SZixu Wang 
115cb5bb285SZixu Wang       if (*NI != *DI)
116cb5bb285SZixu Wang         break;
117cb5bb285SZixu Wang     }
118cb5bb285SZixu Wang     return 0;
119cb5bb285SZixu Wang   };
120cb5bb285SZixu Wang 
121cb5bb285SZixu Wang   unsigned PrefixLength = 0;
122cb5bb285SZixu Wang 
123cb5bb285SZixu Wang   // Go through the search paths and find the first one that is a prefix of
124cb5bb285SZixu Wang   // the header.
125cb5bb285SZixu Wang   for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
126cb5bb285SZixu Wang     // Note whether the match is found in a quoted entry.
127cb5bb285SZixu Wang     if (IsQuoted)
128cb5bb285SZixu Wang       *IsQuoted = Entry.Group == frontend::Quoted;
129cb5bb285SZixu Wang 
130cb5bb285SZixu Wang     if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
131cb5bb285SZixu Wang       if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
132cb5bb285SZixu Wang         // If this is a headermap entry, try to reverse lookup the full path
133cb5bb285SZixu Wang         // for a spelled name before mapping.
134cb5bb285SZixu Wang         StringRef SpelledFilename = HMap->reverseLookupFilename(File);
135cb5bb285SZixu Wang         if (!SpelledFilename.empty())
136cb5bb285SZixu Wang           return SpelledFilename.str();
137cb5bb285SZixu Wang 
138cb5bb285SZixu Wang         // No matching mapping in this headermap, try next search entry.
139cb5bb285SZixu Wang         continue;
140cb5bb285SZixu Wang       }
141cb5bb285SZixu Wang     }
142cb5bb285SZixu Wang 
143cb5bb285SZixu Wang     // Entry is a directory search entry, try to check if it's a prefix of File.
144cb5bb285SZixu Wang     PrefixLength = CheckDir(Entry.Path);
145cb5bb285SZixu Wang     if (PrefixLength > 0) {
146cb5bb285SZixu Wang       // The header is found in a framework path, construct the framework-style
147cb5bb285SZixu Wang       // include name `<Framework/Header.h>`
148cb5bb285SZixu Wang       if (Entry.IsFramework) {
149cb5bb285SZixu Wang         SmallVector<StringRef, 4> Matches;
1504c6043deSCyndy Ishida         clang::installapi::HeaderFile::getFrameworkIncludeRule().match(
1514c6043deSCyndy Ishida             File, &Matches);
152cb5bb285SZixu Wang         // Returned matches are always in stable order.
153cb5bb285SZixu Wang         if (Matches.size() != 4)
1545891420eSKazu Hirata           return std::nullopt;
155cb5bb285SZixu Wang 
156cb5bb285SZixu Wang         return path::convert_to_slash(
157cb5bb285SZixu Wang             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
158cb5bb285SZixu Wang              Matches[3])
159cb5bb285SZixu Wang                 .str());
160cb5bb285SZixu Wang       }
161cb5bb285SZixu Wang 
162cb5bb285SZixu Wang       // The header is found in a normal search path, strip the search path
163cb5bb285SZixu Wang       // prefix to get an include name.
164cb5bb285SZixu Wang       return path::convert_to_slash(File.drop_front(PrefixLength));
165cb5bb285SZixu Wang     }
166cb5bb285SZixu Wang   }
167cb5bb285SZixu Wang 
168cb5bb285SZixu Wang   // Couldn't determine a include name, use full path instead.
1695891420eSKazu Hirata   return std::nullopt;
170cb5bb285SZixu Wang }
171cb5bb285SZixu Wang 
17214e99174SDaniel Grumberg std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
17314e99174SDaniel Grumberg                                                   FileEntryRef FE,
17414e99174SDaniel Grumberg                                                   bool *IsQuoted = nullptr) {
17514e99174SDaniel Grumberg   return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted);
17614e99174SDaniel Grumberg }
17714e99174SDaniel Grumberg 
178aebe5fc6SDaniel Grumberg struct LocationFileChecker {
1797a851921SDaniel Grumberg   bool operator()(SourceLocation Loc) {
180d0dd151eSDaniel Grumberg     // If the loc refers to a macro expansion we need to first get the file
181aebe5fc6SDaniel Grumberg     // location of the expansion.
182cb5bb285SZixu Wang     auto &SM = CI.getSourceManager();
183aebe5fc6SDaniel Grumberg     auto FileLoc = SM.getFileLoc(Loc);
184aebe5fc6SDaniel Grumberg     FileID FID = SM.getFileID(FileLoc);
185aebe5fc6SDaniel Grumberg     if (FID.isInvalid())
186aebe5fc6SDaniel Grumberg       return false;
187aebe5fc6SDaniel Grumberg 
1885a3130e3SJan Svoboda     OptionalFileEntryRef File = SM.getFileEntryRefForID(FID);
189aebe5fc6SDaniel Grumberg     if (!File)
190aebe5fc6SDaniel Grumberg       return false;
191aebe5fc6SDaniel Grumberg 
1925a3130e3SJan Svoboda     if (KnownFileEntries.count(*File))
193aebe5fc6SDaniel Grumberg       return true;
194aebe5fc6SDaniel Grumberg 
1955a3130e3SJan Svoboda     if (ExternalFileEntries.count(*File))
196cb5bb285SZixu Wang       return false;
197cb5bb285SZixu Wang 
198cb5bb285SZixu Wang     // Try to reduce the include name the same way we tried to include it.
199cb5bb285SZixu Wang     bool IsQuoted = false;
20014e99174SDaniel Grumberg     if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted))
201f13019f8SKazu Hirata       if (llvm::any_of(KnownFiles,
202cb5bb285SZixu Wang                        [&IsQuoted, &IncludeName](const auto &KnownFile) {
203cb5bb285SZixu Wang                          return KnownFile.first.equals(*IncludeName) &&
204cb5bb285SZixu Wang                                 KnownFile.second == IsQuoted;
205f13019f8SKazu Hirata                        })) {
2065a3130e3SJan Svoboda         KnownFileEntries.insert(*File);
207cb5bb285SZixu Wang         return true;
208cb5bb285SZixu Wang       }
209cb5bb285SZixu Wang 
210cb5bb285SZixu Wang     // Record that the file was not found to avoid future reverse lookup for
211cb5bb285SZixu Wang     // the same file.
2125a3130e3SJan Svoboda     ExternalFileEntries.insert(*File);
213aebe5fc6SDaniel Grumberg     return false;
214aebe5fc6SDaniel Grumberg   }
215aebe5fc6SDaniel Grumberg 
216cb5bb285SZixu Wang   LocationFileChecker(const CompilerInstance &CI,
217cb5bb285SZixu Wang                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
218cb5bb285SZixu Wang       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
219cb5bb285SZixu Wang     for (const auto &KnownFile : KnownFiles)
220*b1aea98cSJan Svoboda       if (auto FE = CI.getFileManager().getOptionalFileRef(KnownFile.first))
221*b1aea98cSJan Svoboda         KnownFileEntries.insert(*FE);
222aebe5fc6SDaniel Grumberg   }
223aebe5fc6SDaniel Grumberg 
224aebe5fc6SDaniel Grumberg private:
225cb5bb285SZixu Wang   const CompilerInstance &CI;
226cb5bb285SZixu Wang   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
227aebe5fc6SDaniel Grumberg   llvm::DenseSet<const FileEntry *> KnownFileEntries;
228cb5bb285SZixu Wang   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
229aebe5fc6SDaniel Grumberg };
230aebe5fc6SDaniel Grumberg 
231142c3d9dSDaniel Grumberg struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
232142c3d9dSDaniel Grumberg   bool shouldDeclBeIncluded(const Decl *D) const {
233142c3d9dSDaniel Grumberg     bool ShouldBeIncluded = true;
234142c3d9dSDaniel Grumberg     // Check that we have the definition for redeclarable types.
235142c3d9dSDaniel Grumberg     if (auto *TD = llvm::dyn_cast<TagDecl>(D))
236142c3d9dSDaniel Grumberg       ShouldBeIncluded = TD->isThisDeclarationADefinition();
237142c3d9dSDaniel Grumberg     else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
238142c3d9dSDaniel Grumberg       ShouldBeIncluded = Interface->isThisDeclarationADefinition();
239142c3d9dSDaniel Grumberg     else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
240142c3d9dSDaniel Grumberg       ShouldBeIncluded = Protocol->isThisDeclarationADefinition();
241142c3d9dSDaniel Grumberg 
242142c3d9dSDaniel Grumberg     ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation());
243142c3d9dSDaniel Grumberg     return ShouldBeIncluded;
244142c3d9dSDaniel Grumberg   }
245142c3d9dSDaniel Grumberg 
246142c3d9dSDaniel Grumberg   BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context,
247142c3d9dSDaniel Grumberg                          APISet &API)
248142c3d9dSDaniel Grumberg       : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {}
249142c3d9dSDaniel Grumberg 
250142c3d9dSDaniel Grumberg private:
251142c3d9dSDaniel Grumberg   LocationFileChecker &LCF;
252142c3d9dSDaniel Grumberg };
253142c3d9dSDaniel Grumberg 
2548e9145e4SAnkur class WrappingExtractAPIConsumer : public ASTConsumer {
2558e9145e4SAnkur public:
2568e9145e4SAnkur   WrappingExtractAPIConsumer(ASTContext &Context, APISet &API)
2578e9145e4SAnkur       : Visitor(Context, API) {}
2588e9145e4SAnkur 
2598e9145e4SAnkur   void HandleTranslationUnit(ASTContext &Context) override {
2608e9145e4SAnkur     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
2618e9145e4SAnkur     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
2628e9145e4SAnkur   }
2638e9145e4SAnkur 
2648e9145e4SAnkur private:
2658e9145e4SAnkur   ExtractAPIVisitor<> Visitor;
2668e9145e4SAnkur };
2678e9145e4SAnkur 
26889f6b26fSZixu Wang class ExtractAPIConsumer : public ASTConsumer {
26989f6b26fSZixu Wang public:
270aebe5fc6SDaniel Grumberg   ExtractAPIConsumer(ASTContext &Context,
271aebe5fc6SDaniel Grumberg                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
272142c3d9dSDaniel Grumberg       : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {}
27389f6b26fSZixu Wang 
27489f6b26fSZixu Wang   void HandleTranslationUnit(ASTContext &Context) override {
27589f6b26fSZixu Wang     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
27689f6b26fSZixu Wang     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
27789f6b26fSZixu Wang   }
27889f6b26fSZixu Wang 
27989f6b26fSZixu Wang private:
280142c3d9dSDaniel Grumberg   BatchExtractAPIVisitor Visitor;
281aebe5fc6SDaniel Grumberg   std::unique_ptr<LocationFileChecker> LCF;
28289f6b26fSZixu Wang };
28389f6b26fSZixu Wang 
284529a0570SDaniel Grumberg class MacroCallback : public PPCallbacks {
285529a0570SDaniel Grumberg public:
2868e9145e4SAnkur   MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP)
2878e9145e4SAnkur       : SM(SM), API(API), PP(PP) {}
288529a0570SDaniel Grumberg 
289529a0570SDaniel Grumberg   void EndOfMainFile() override {
290b1b24d75SDaniel Grumberg     for (const auto &M : PP.macros()) {
291b1b24d75SDaniel Grumberg       auto *II = M.getFirst();
292b1b24d75SDaniel Grumberg       auto MD = PP.getMacroDefinition(II);
293b1b24d75SDaniel Grumberg       auto *MI = MD.getMacroInfo();
294b1b24d75SDaniel Grumberg 
295b1b24d75SDaniel Grumberg       if (!MI)
296529a0570SDaniel Grumberg         continue;
297529a0570SDaniel Grumberg 
298b1b24d75SDaniel Grumberg       // Ignore header guard macros
299b1b24d75SDaniel Grumberg       if (MI->isUsedForHeaderGuard())
300aebe5fc6SDaniel Grumberg         continue;
301aebe5fc6SDaniel Grumberg 
302b1b24d75SDaniel Grumberg       // Ignore builtin macros and ones defined via the command line.
303b1b24d75SDaniel Grumberg       if (MI->isBuiltinMacro())
304b1b24d75SDaniel Grumberg         continue;
305b1b24d75SDaniel Grumberg 
306b1b24d75SDaniel Grumberg       auto DefLoc = MI->getDefinitionLoc();
307b1b24d75SDaniel Grumberg 
308b1b24d75SDaniel Grumberg       if (SM.isWrittenInBuiltinFile(DefLoc) ||
309b1b24d75SDaniel Grumberg           SM.isWrittenInCommandLineFile(DefLoc))
310b1b24d75SDaniel Grumberg         continue;
311b1b24d75SDaniel Grumberg 
312b1b24d75SDaniel Grumberg       auto AssociatedModuleMacros = MD.getModuleMacros();
313b1b24d75SDaniel Grumberg       StringRef OwningModuleName;
314b1b24d75SDaniel Grumberg       if (!AssociatedModuleMacros.empty())
315b1b24d75SDaniel Grumberg         OwningModuleName = AssociatedModuleMacros.back()
316b1b24d75SDaniel Grumberg                                ->getOwningModule()
317b1b24d75SDaniel Grumberg                                ->getTopLevelModuleName();
318b1b24d75SDaniel Grumberg 
319b1b24d75SDaniel Grumberg       if (!shouldMacroBeIncluded(DefLoc, OwningModuleName))
320b1b24d75SDaniel Grumberg         continue;
321b1b24d75SDaniel Grumberg 
322b1b24d75SDaniel Grumberg       StringRef Name = II->getName();
323b1b24d75SDaniel Grumberg       PresumedLoc Loc = SM.getPresumedLoc(DefLoc);
324e05c1b46SDaniel Grumberg       SmallString<128> USR;
325b1b24d75SDaniel Grumberg       index::generateUSRForMacro(Name, DefLoc, SM, USR);
326e05c1b46SDaniel Grumberg       API.createRecord<extractapi::MacroDefinitionRecord>(
327e05c1b46SDaniel Grumberg           USR, Name, SymbolReference(), Loc,
328b1b24d75SDaniel Grumberg           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, MI),
3297a851921SDaniel Grumberg           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
330b1b24d75SDaniel Grumberg           SM.isInSystemHeader(DefLoc));
331b1b24d75SDaniel Grumberg     }
332529a0570SDaniel Grumberg   }
333529a0570SDaniel Grumberg 
334b1b24d75SDaniel Grumberg   virtual bool shouldMacroBeIncluded(const SourceLocation &MacroLoc,
335b1b24d75SDaniel Grumberg                                      StringRef ModuleName) {
336b1b24d75SDaniel Grumberg     return true;
337529a0570SDaniel Grumberg   }
338529a0570SDaniel Grumberg 
339529a0570SDaniel Grumberg   const SourceManager &SM;
340529a0570SDaniel Grumberg   APISet &API;
34110155922SDaniel Grumberg   Preprocessor &PP;
342529a0570SDaniel Grumberg };
343529a0570SDaniel Grumberg 
3448e9145e4SAnkur class APIMacroCallback : public MacroCallback {
3458e9145e4SAnkur public:
3468e9145e4SAnkur   APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP,
3478e9145e4SAnkur                    LocationFileChecker &LCF)
3488e9145e4SAnkur       : MacroCallback(SM, API, PP), LCF(LCF) {}
3498e9145e4SAnkur 
350b1b24d75SDaniel Grumberg   bool shouldMacroBeIncluded(const SourceLocation &MacroLoc,
351b1b24d75SDaniel Grumberg                              StringRef ModuleName) override {
3528e9145e4SAnkur     // Do not include macros from external files
35386835d2dSDaniel Grumberg     return LCF(MacroLoc);
3548e9145e4SAnkur   }
3558e9145e4SAnkur 
3568e9145e4SAnkur private:
3578e9145e4SAnkur   LocationFileChecker &LCF;
3588e9145e4SAnkur };
3598e9145e4SAnkur 
360e05c1b46SDaniel Grumberg std::unique_ptr<llvm::raw_pwrite_stream>
361e05c1b46SDaniel Grumberg createAdditionalSymbolGraphFile(CompilerInstance &CI, Twine BaseName) {
362e05c1b46SDaniel Grumberg   auto OutputDirectory = CI.getFrontendOpts().SymbolGraphOutputDir;
363b31414bfSDaniel Grumberg 
364e05c1b46SDaniel Grumberg   SmallString<256> FileName;
365e05c1b46SDaniel Grumberg   llvm::sys::path::append(FileName, OutputDirectory,
366e05c1b46SDaniel Grumberg                           BaseName + ".symbols.json");
367e05c1b46SDaniel Grumberg   return CI.createOutputFile(
368e05c1b46SDaniel Grumberg       FileName, /*Binary*/ false, /*RemoveFileOnSignal*/ false,
369e05c1b46SDaniel Grumberg       /*UseTemporary*/ true, /*CreateMissingDirectories*/ true);
370b31414bfSDaniel Grumberg }
371b31414bfSDaniel Grumberg 
372e05c1b46SDaniel Grumberg } // namespace
373e05c1b46SDaniel Grumberg 
374e05c1b46SDaniel Grumberg void ExtractAPIActionBase::ImplEndSourceFileAction(CompilerInstance &CI) {
375e05c1b46SDaniel Grumberg   SymbolGraphSerializerOption SerializationOptions;
376e05c1b46SDaniel Grumberg   SerializationOptions.Compact = !CI.getFrontendOpts().EmitPrettySymbolGraphs;
377e05c1b46SDaniel Grumberg   SerializationOptions.EmitSymbolLabelsForTesting =
378e05c1b46SDaniel Grumberg       CI.getFrontendOpts().EmitSymbolGraphSymbolLabelsForTesting;
379e05c1b46SDaniel Grumberg 
380e05c1b46SDaniel Grumberg   if (CI.getFrontendOpts().EmitExtensionSymbolGraphs) {
381e05c1b46SDaniel Grumberg     auto ConstructOutputFile = [&CI](Twine BaseName) {
382e05c1b46SDaniel Grumberg       return createAdditionalSymbolGraphFile(CI, BaseName);
383e05c1b46SDaniel Grumberg     };
384e05c1b46SDaniel Grumberg 
385e05c1b46SDaniel Grumberg     SymbolGraphSerializer::serializeWithExtensionGraphs(
386e05c1b46SDaniel Grumberg         *OS, *API, IgnoresList, ConstructOutputFile, SerializationOptions);
387e05c1b46SDaniel Grumberg   } else {
388e05c1b46SDaniel Grumberg     SymbolGraphSerializer::serializeMainSymbolGraph(*OS, *API, IgnoresList,
389e05c1b46SDaniel Grumberg                                                     SerializationOptions);
390e05c1b46SDaniel Grumberg   }
391e05c1b46SDaniel Grumberg 
392e05c1b46SDaniel Grumberg   // Flush the stream and close the main output stream.
393e05c1b46SDaniel Grumberg   OS.reset();
3948e9145e4SAnkur }
3958e9145e4SAnkur 
39689f6b26fSZixu Wang std::unique_ptr<ASTConsumer>
39789f6b26fSZixu Wang ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
398e05c1b46SDaniel Grumberg   auto ProductName = CI.getFrontendOpts().ProductName;
399e05c1b46SDaniel Grumberg 
400e05c1b46SDaniel Grumberg   if (CI.getFrontendOpts().SymbolGraphOutputDir.empty())
401e05c1b46SDaniel Grumberg     OS = CI.createDefaultOutputFile(/*Binary*/ false, InFile,
402e05c1b46SDaniel Grumberg                                     /*Extension*/ "symbols.json",
403e05c1b46SDaniel Grumberg                                     /*RemoveFileOnSignal*/ false,
404e05c1b46SDaniel Grumberg                                     /*CreateMissingDirectories*/ true);
405e05c1b46SDaniel Grumberg   else
406e05c1b46SDaniel Grumberg     OS = createAdditionalSymbolGraphFile(CI, ProductName);
4078e9145e4SAnkur 
40889f6b26fSZixu Wang   if (!OS)
40989f6b26fSZixu Wang     return nullptr;
410a9909d23SDaniel Grumberg 
411a9909d23SDaniel Grumberg   // Now that we have enough information about the language options and the
412a9909d23SDaniel Grumberg   // target triple, let's create the APISet before anyone uses it.
413a9909d23SDaniel Grumberg   API = std::make_unique<APISet>(
414a9909d23SDaniel Grumberg       CI.getTarget().getTriple(),
4157a851921SDaniel Grumberg       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
416a9909d23SDaniel Grumberg 
417cb5bb285SZixu Wang   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
418aebe5fc6SDaniel Grumberg 
4198e9145e4SAnkur   CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>(
4208e9145e4SAnkur       CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF));
421529a0570SDaniel Grumberg 
4225301826fSZixu Wang   // Do not include location in anonymous decls.
4235301826fSZixu Wang   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
4245301826fSZixu Wang   Policy.AnonymousTagLocations = false;
4255301826fSZixu Wang   CI.getASTContext().setPrintingPolicy(Policy);
4265301826fSZixu Wang 
42758825d2cSAnkur   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
428791fe26dSDaniel Grumberg     llvm::handleAllErrors(
42958825d2cSAnkur         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
430791fe26dSDaniel Grumberg                                CI.getFileManager())
431791fe26dSDaniel Grumberg             .moveInto(IgnoresList),
432791fe26dSDaniel Grumberg         [&CI](const IgnoresFileNotFound &Err) {
433791fe26dSDaniel Grumberg           CI.getDiagnostics().Report(
434791fe26dSDaniel Grumberg               diag::err_extract_api_ignores_file_not_found)
435791fe26dSDaniel Grumberg               << Err.Path;
436791fe26dSDaniel Grumberg         });
437791fe26dSDaniel Grumberg   }
438791fe26dSDaniel Grumberg 
439aebe5fc6SDaniel Grumberg   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
440aebe5fc6SDaniel Grumberg                                               std::move(LCF), *API);
44189f6b26fSZixu Wang }
44289f6b26fSZixu Wang 
443f833aab0SDaniel Grumberg bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
444f833aab0SDaniel Grumberg   auto &Inputs = CI.getFrontendOpts().Inputs;
445f833aab0SDaniel Grumberg   if (Inputs.empty())
446f833aab0SDaniel Grumberg     return true;
447f833aab0SDaniel Grumberg 
448cb5bb285SZixu Wang   if (!CI.hasFileManager())
449cb5bb285SZixu Wang     if (!CI.createFileManager())
450cb5bb285SZixu Wang       return false;
451cb5bb285SZixu Wang 
452f833aab0SDaniel Grumberg   auto Kind = Inputs[0].getKind();
453f833aab0SDaniel Grumberg 
454f833aab0SDaniel Grumberg   // Convert the header file inputs into a single input buffer.
455f833aab0SDaniel Grumberg   SmallString<256> HeaderContents;
456cb5bb285SZixu Wang   bool IsQuoted = false;
457f833aab0SDaniel Grumberg   for (const FrontendInputFile &FIF : Inputs) {
458f833aab0SDaniel Grumberg     if (Kind.isObjectiveC())
459f833aab0SDaniel Grumberg       HeaderContents += "#import";
460f833aab0SDaniel Grumberg     else
461f833aab0SDaniel Grumberg       HeaderContents += "#include";
4624c262feeSZixu Wang 
463cb5bb285SZixu Wang     StringRef FilePath = FIF.getFile();
464cb5bb285SZixu Wang     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
465cb5bb285SZixu Wang       if (IsQuoted)
466cb5bb285SZixu Wang         HeaderContents += " \"";
467cb5bb285SZixu Wang       else
468cb5bb285SZixu Wang         HeaderContents += " <";
469cb5bb285SZixu Wang 
470cb5bb285SZixu Wang       HeaderContents += *RelativeName;
471cb5bb285SZixu Wang 
472cb5bb285SZixu Wang       if (IsQuoted)
473cb5bb285SZixu Wang         HeaderContents += "\"\n";
474cb5bb285SZixu Wang       else
475cb5bb285SZixu Wang         HeaderContents += ">\n";
476cc344d26SAdrian Kuegel       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
477cc344d26SAdrian Kuegel                                    IsQuoted);
478cb5bb285SZixu Wang     } else {
479cb5bb285SZixu Wang       HeaderContents += " \"";
480cb5bb285SZixu Wang       HeaderContents += FilePath;
481cb5bb285SZixu Wang       HeaderContents += "\"\n";
482cb5bb285SZixu Wang       KnownInputFiles.emplace_back(FilePath, true);
4832966f0faSZixu Wang     }
484cb5bb285SZixu Wang   }
485cb5bb285SZixu Wang 
486cb5bb285SZixu Wang   if (CI.getHeaderSearchOpts().Verbose)
487cb5bb285SZixu Wang     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
488cb5bb285SZixu Wang                                 << HeaderContents << "\n";
489f833aab0SDaniel Grumberg 
490985eaa1aSDaniel Grumberg   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
491f833aab0SDaniel Grumberg                                                 getInputBufferName());
492f833aab0SDaniel Grumberg 
493f833aab0SDaniel Grumberg   // Set that buffer up as our "real" input in the CompilerInstance.
494f833aab0SDaniel Grumberg   Inputs.clear();
495f833aab0SDaniel Grumberg   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
496f833aab0SDaniel Grumberg 
497f833aab0SDaniel Grumberg   return true;
498f833aab0SDaniel Grumberg }
499f833aab0SDaniel Grumberg 
500e05c1b46SDaniel Grumberg void ExtractAPIAction::EndSourceFileAction() {
501e05c1b46SDaniel Grumberg   ImplEndSourceFileAction(getCompilerInstance());
502e05c1b46SDaniel Grumberg }
503a9909d23SDaniel Grumberg 
5048e9145e4SAnkur std::unique_ptr<ASTConsumer>
5058e9145e4SAnkur WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
5068e9145e4SAnkur                                             StringRef InFile) {
5078e9145e4SAnkur   auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
5088e9145e4SAnkur   if (!OtherConsumer)
5098e9145e4SAnkur     return nullptr;
5108e9145e4SAnkur 
5118e9145e4SAnkur   CreatedASTConsumer = true;
5128e9145e4SAnkur 
513e05c1b46SDaniel Grumberg   ProductName = CI.getFrontendOpts().ProductName;
514e05c1b46SDaniel Grumberg   auto InputFilename = llvm::sys::path::filename(InFile);
515e05c1b46SDaniel Grumberg   OS = createAdditionalSymbolGraphFile(CI, InputFilename);
5168e9145e4SAnkur 
5178e9145e4SAnkur   // Now that we have enough information about the language options and the
5188e9145e4SAnkur   // target triple, let's create the APISet before anyone uses it.
5198e9145e4SAnkur   API = std::make_unique<APISet>(
5208e9145e4SAnkur       CI.getTarget().getTriple(),
5218e9145e4SAnkur       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
5228e9145e4SAnkur 
5238e9145e4SAnkur   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
5248e9145e4SAnkur       CI.getSourceManager(), *API, CI.getPreprocessor()));
5258e9145e4SAnkur 
5268e9145e4SAnkur   // Do not include location in anonymous decls.
5278e9145e4SAnkur   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
5288e9145e4SAnkur   Policy.AnonymousTagLocations = false;
5298e9145e4SAnkur   CI.getASTContext().setPrintingPolicy(Policy);
5308e9145e4SAnkur 
5318e9145e4SAnkur   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
5328e9145e4SAnkur     llvm::handleAllErrors(
5338e9145e4SAnkur         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
5348e9145e4SAnkur                                CI.getFileManager())
5358e9145e4SAnkur             .moveInto(IgnoresList),
5368e9145e4SAnkur         [&CI](const IgnoresFileNotFound &Err) {
5378e9145e4SAnkur           CI.getDiagnostics().Report(
5388e9145e4SAnkur               diag::err_extract_api_ignores_file_not_found)
5398e9145e4SAnkur               << Err.Path;
5408e9145e4SAnkur         });
5418e9145e4SAnkur   }
5428e9145e4SAnkur 
5438e9145e4SAnkur   auto WrappingConsumer =
5448e9145e4SAnkur       std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API);
5458e9145e4SAnkur   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
5468e9145e4SAnkur   Consumers.push_back(std::move(OtherConsumer));
5478e9145e4SAnkur   Consumers.push_back(std::move(WrappingConsumer));
5488e9145e4SAnkur 
5498e9145e4SAnkur   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
5508e9145e4SAnkur }
5518e9145e4SAnkur 
5528e9145e4SAnkur void WrappingExtractAPIAction::EndSourceFileAction() {
5538e9145e4SAnkur   // Invoke wrapped action's method.
5548e9145e4SAnkur   WrapperFrontendAction::EndSourceFileAction();
5558e9145e4SAnkur 
5568e9145e4SAnkur   if (CreatedASTConsumer) {
557e05c1b46SDaniel Grumberg     ImplEndSourceFileAction(getCompilerInstance());
5588e9145e4SAnkur   }
559a9909d23SDaniel Grumberg }
560