181ad6265SDimitry Andric //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric ///
981ad6265SDimitry Andric /// \file
10bdd1243dSDimitry Andric /// This file implements the ExtractAPIAction, and ASTConsumer to collect API
11bdd1243dSDimitry Andric /// information.
1281ad6265SDimitry Andric ///
1381ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1481ad6265SDimitry Andric 
15fe013be4SDimitry Andric #include "clang/AST/ASTConcept.h"
1681ad6265SDimitry Andric #include "clang/AST/ASTConsumer.h"
1781ad6265SDimitry Andric #include "clang/AST/ASTContext.h"
18fe013be4SDimitry Andric #include "clang/AST/DeclObjC.h"
19bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticFrontend.h"
20*c9157d92SDimitry Andric #include "clang/Basic/FileEntry.h"
2181ad6265SDimitry Andric #include "clang/Basic/SourceLocation.h"
2281ad6265SDimitry Andric #include "clang/Basic/SourceManager.h"
2381ad6265SDimitry Andric #include "clang/Basic/TargetInfo.h"
2481ad6265SDimitry Andric #include "clang/ExtractAPI/API.h"
25bdd1243dSDimitry Andric #include "clang/ExtractAPI/APIIgnoresList.h"
26bdd1243dSDimitry Andric #include "clang/ExtractAPI/ExtractAPIVisitor.h"
2781ad6265SDimitry Andric #include "clang/ExtractAPI/FrontendActions.h"
2881ad6265SDimitry Andric #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
2981ad6265SDimitry Andric #include "clang/Frontend/ASTConsumers.h"
3081ad6265SDimitry Andric #include "clang/Frontend/CompilerInstance.h"
3181ad6265SDimitry Andric #include "clang/Frontend/FrontendOptions.h"
32fe013be4SDimitry Andric #include "clang/Frontend/MultiplexConsumer.h"
3381ad6265SDimitry Andric #include "clang/Lex/MacroInfo.h"
3481ad6265SDimitry Andric #include "clang/Lex/PPCallbacks.h"
3581ad6265SDimitry Andric #include "clang/Lex/Preprocessor.h"
3681ad6265SDimitry Andric #include "clang/Lex/PreprocessorOptions.h"
3781ad6265SDimitry Andric #include "llvm/ADT/DenseSet.h"
3881ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h"
39fe013be4SDimitry Andric #include "llvm/ADT/SmallString.h"
4081ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h"
41fe013be4SDimitry Andric #include "llvm/Support/Casting.h"
42bdd1243dSDimitry Andric #include "llvm/Support/Error.h"
4381ad6265SDimitry Andric #include "llvm/Support/FileSystem.h"
4481ad6265SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
4581ad6265SDimitry Andric #include "llvm/Support/Path.h"
4681ad6265SDimitry Andric #include "llvm/Support/Regex.h"
4781ad6265SDimitry Andric #include "llvm/Support/raw_ostream.h"
4881ad6265SDimitry Andric #include <memory>
49bdd1243dSDimitry Andric #include <optional>
5081ad6265SDimitry Andric #include <utility>
5181ad6265SDimitry Andric 
5281ad6265SDimitry Andric using namespace clang;
5381ad6265SDimitry Andric using namespace extractapi;
5481ad6265SDimitry Andric 
5581ad6265SDimitry Andric namespace {
5681ad6265SDimitry Andric 
getRelativeIncludeName(const CompilerInstance & CI,StringRef File,bool * IsQuoted=nullptr)57bdd1243dSDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
5881ad6265SDimitry Andric                                                   StringRef File,
5981ad6265SDimitry Andric                                                   bool *IsQuoted = nullptr) {
6081ad6265SDimitry Andric   assert(CI.hasFileManager() &&
6181ad6265SDimitry Andric          "CompilerInstance does not have a FileNamager!");
6281ad6265SDimitry Andric 
6381ad6265SDimitry Andric   using namespace llvm::sys;
6481ad6265SDimitry Andric   // Matches framework include patterns
6581ad6265SDimitry Andric   const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
6681ad6265SDimitry Andric 
6781ad6265SDimitry Andric   const auto &FS = CI.getVirtualFileSystem();
6881ad6265SDimitry Andric 
6981ad6265SDimitry Andric   SmallString<128> FilePath(File.begin(), File.end());
7081ad6265SDimitry Andric   FS.makeAbsolute(FilePath);
7181ad6265SDimitry Andric   path::remove_dots(FilePath, true);
7281ad6265SDimitry Andric   FilePath = path::convert_to_slash(FilePath);
7381ad6265SDimitry Andric   File = FilePath;
7481ad6265SDimitry Andric 
7581ad6265SDimitry Andric   // Checks whether `Dir` is a strict path prefix of `File`. If so returns
7681ad6265SDimitry Andric   // the prefix length. Otherwise return 0.
7781ad6265SDimitry Andric   auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
7881ad6265SDimitry Andric     llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
7981ad6265SDimitry Andric     FS.makeAbsolute(DirPath);
8081ad6265SDimitry Andric     path::remove_dots(DirPath, true);
8181ad6265SDimitry Andric     Dir = DirPath;
8281ad6265SDimitry Andric     for (auto NI = path::begin(File), NE = path::end(File),
8381ad6265SDimitry Andric               DI = path::begin(Dir), DE = path::end(Dir);
8481ad6265SDimitry Andric          /*termination condition in loop*/; ++NI, ++DI) {
8581ad6265SDimitry Andric       // '.' components in File are ignored.
8681ad6265SDimitry Andric       while (NI != NE && *NI == ".")
8781ad6265SDimitry Andric         ++NI;
8881ad6265SDimitry Andric       if (NI == NE)
8981ad6265SDimitry Andric         break;
9081ad6265SDimitry Andric 
9181ad6265SDimitry Andric       // '.' components in Dir are ignored.
9281ad6265SDimitry Andric       while (DI != DE && *DI == ".")
9381ad6265SDimitry Andric         ++DI;
9481ad6265SDimitry Andric 
9581ad6265SDimitry Andric       // Dir is a prefix of File, up to '.' components and choice of path
9681ad6265SDimitry Andric       // separators.
9781ad6265SDimitry Andric       if (DI == DE)
9881ad6265SDimitry Andric         return NI - path::begin(File);
9981ad6265SDimitry Andric 
10081ad6265SDimitry Andric       // Consider all path separators equal.
10181ad6265SDimitry Andric       if (NI->size() == 1 && DI->size() == 1 &&
10281ad6265SDimitry Andric           path::is_separator(NI->front()) && path::is_separator(DI->front()))
10381ad6265SDimitry Andric         continue;
10481ad6265SDimitry Andric 
10581ad6265SDimitry Andric       // Special case Apple .sdk folders since the search path is typically a
10681ad6265SDimitry Andric       // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
10781ad6265SDimitry Andric       // located in `iPhoneSimulator.sdk` (the real folder).
108*c9157d92SDimitry Andric       if (NI->ends_with(".sdk") && DI->ends_with(".sdk")) {
10981ad6265SDimitry Andric         StringRef NBasename = path::stem(*NI);
11081ad6265SDimitry Andric         StringRef DBasename = path::stem(*DI);
111*c9157d92SDimitry Andric         if (DBasename.starts_with(NBasename))
11281ad6265SDimitry Andric           continue;
11381ad6265SDimitry Andric       }
11481ad6265SDimitry Andric 
11581ad6265SDimitry Andric       if (*NI != *DI)
11681ad6265SDimitry Andric         break;
11781ad6265SDimitry Andric     }
11881ad6265SDimitry Andric     return 0;
11981ad6265SDimitry Andric   };
12081ad6265SDimitry Andric 
12181ad6265SDimitry Andric   unsigned PrefixLength = 0;
12281ad6265SDimitry Andric 
12381ad6265SDimitry Andric   // Go through the search paths and find the first one that is a prefix of
12481ad6265SDimitry Andric   // the header.
12581ad6265SDimitry Andric   for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
12681ad6265SDimitry Andric     // Note whether the match is found in a quoted entry.
12781ad6265SDimitry Andric     if (IsQuoted)
12881ad6265SDimitry Andric       *IsQuoted = Entry.Group == frontend::Quoted;
12981ad6265SDimitry Andric 
13081ad6265SDimitry Andric     if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
13181ad6265SDimitry Andric       if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
13281ad6265SDimitry Andric         // If this is a headermap entry, try to reverse lookup the full path
13381ad6265SDimitry Andric         // for a spelled name before mapping.
13481ad6265SDimitry Andric         StringRef SpelledFilename = HMap->reverseLookupFilename(File);
13581ad6265SDimitry Andric         if (!SpelledFilename.empty())
13681ad6265SDimitry Andric           return SpelledFilename.str();
13781ad6265SDimitry Andric 
13881ad6265SDimitry Andric         // No matching mapping in this headermap, try next search entry.
13981ad6265SDimitry Andric         continue;
14081ad6265SDimitry Andric       }
14181ad6265SDimitry Andric     }
14281ad6265SDimitry Andric 
14381ad6265SDimitry Andric     // Entry is a directory search entry, try to check if it's a prefix of File.
14481ad6265SDimitry Andric     PrefixLength = CheckDir(Entry.Path);
14581ad6265SDimitry Andric     if (PrefixLength > 0) {
14681ad6265SDimitry Andric       // The header is found in a framework path, construct the framework-style
14781ad6265SDimitry Andric       // include name `<Framework/Header.h>`
14881ad6265SDimitry Andric       if (Entry.IsFramework) {
14981ad6265SDimitry Andric         SmallVector<StringRef, 4> Matches;
15081ad6265SDimitry Andric         Rule.match(File, &Matches);
15181ad6265SDimitry Andric         // Returned matches are always in stable order.
15281ad6265SDimitry Andric         if (Matches.size() != 4)
153bdd1243dSDimitry Andric           return std::nullopt;
15481ad6265SDimitry Andric 
15581ad6265SDimitry Andric         return path::convert_to_slash(
15681ad6265SDimitry Andric             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
15781ad6265SDimitry Andric              Matches[3])
15881ad6265SDimitry Andric                 .str());
15981ad6265SDimitry Andric       }
16081ad6265SDimitry Andric 
16181ad6265SDimitry Andric       // The header is found in a normal search path, strip the search path
16281ad6265SDimitry Andric       // prefix to get an include name.
16381ad6265SDimitry Andric       return path::convert_to_slash(File.drop_front(PrefixLength));
16481ad6265SDimitry Andric     }
16581ad6265SDimitry Andric   }
16681ad6265SDimitry Andric 
16781ad6265SDimitry Andric   // Couldn't determine a include name, use full path instead.
168bdd1243dSDimitry Andric   return std::nullopt;
16981ad6265SDimitry Andric }
17081ad6265SDimitry Andric 
getRelativeIncludeName(const CompilerInstance & CI,FileEntryRef FE,bool * IsQuoted=nullptr)171*c9157d92SDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
172*c9157d92SDimitry Andric                                                   FileEntryRef FE,
173*c9157d92SDimitry Andric                                                   bool *IsQuoted = nullptr) {
174*c9157d92SDimitry Andric   return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted);
175*c9157d92SDimitry Andric }
176*c9157d92SDimitry Andric 
17781ad6265SDimitry Andric struct LocationFileChecker {
operator ()__anon5832f3d50111::LocationFileChecker178bdd1243dSDimitry Andric   bool operator()(SourceLocation Loc) {
17981ad6265SDimitry Andric     // If the loc refers to a macro expansion we need to first get the file
18081ad6265SDimitry Andric     // location of the expansion.
18181ad6265SDimitry Andric     auto &SM = CI.getSourceManager();
18281ad6265SDimitry Andric     auto FileLoc = SM.getFileLoc(Loc);
18381ad6265SDimitry Andric     FileID FID = SM.getFileID(FileLoc);
18481ad6265SDimitry Andric     if (FID.isInvalid())
18581ad6265SDimitry Andric       return false;
18681ad6265SDimitry Andric 
187*c9157d92SDimitry Andric     OptionalFileEntryRef File = SM.getFileEntryRefForID(FID);
18881ad6265SDimitry Andric     if (!File)
18981ad6265SDimitry Andric       return false;
19081ad6265SDimitry Andric 
191*c9157d92SDimitry Andric     if (KnownFileEntries.count(*File))
19281ad6265SDimitry Andric       return true;
19381ad6265SDimitry Andric 
194*c9157d92SDimitry Andric     if (ExternalFileEntries.count(*File))
19581ad6265SDimitry Andric       return false;
19681ad6265SDimitry Andric 
19781ad6265SDimitry Andric     // Try to reduce the include name the same way we tried to include it.
19881ad6265SDimitry Andric     bool IsQuoted = false;
199*c9157d92SDimitry Andric     if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted))
20081ad6265SDimitry Andric       if (llvm::any_of(KnownFiles,
20181ad6265SDimitry Andric                        [&IsQuoted, &IncludeName](const auto &KnownFile) {
20281ad6265SDimitry Andric                          return KnownFile.first.equals(*IncludeName) &&
20381ad6265SDimitry Andric                                 KnownFile.second == IsQuoted;
20481ad6265SDimitry Andric                        })) {
205*c9157d92SDimitry Andric         KnownFileEntries.insert(*File);
20681ad6265SDimitry Andric         return true;
20781ad6265SDimitry Andric       }
20881ad6265SDimitry Andric 
20981ad6265SDimitry Andric     // Record that the file was not found to avoid future reverse lookup for
21081ad6265SDimitry Andric     // the same file.
211*c9157d92SDimitry Andric     ExternalFileEntries.insert(*File);
21281ad6265SDimitry Andric     return false;
21381ad6265SDimitry Andric   }
21481ad6265SDimitry Andric 
LocationFileChecker__anon5832f3d50111::LocationFileChecker21581ad6265SDimitry Andric   LocationFileChecker(const CompilerInstance &CI,
21681ad6265SDimitry Andric                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
21781ad6265SDimitry Andric       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
21881ad6265SDimitry Andric     for (const auto &KnownFile : KnownFiles)
21981ad6265SDimitry Andric       if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
22081ad6265SDimitry Andric         KnownFileEntries.insert(*FileEntry);
22181ad6265SDimitry Andric   }
22281ad6265SDimitry Andric 
22381ad6265SDimitry Andric private:
22481ad6265SDimitry Andric   const CompilerInstance &CI;
22581ad6265SDimitry Andric   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
22681ad6265SDimitry Andric   llvm::DenseSet<const FileEntry *> KnownFileEntries;
22781ad6265SDimitry Andric   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
22881ad6265SDimitry Andric };
22981ad6265SDimitry Andric 
230fe013be4SDimitry Andric struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
shouldDeclBeIncluded__anon5832f3d50111::BatchExtractAPIVisitor231fe013be4SDimitry Andric   bool shouldDeclBeIncluded(const Decl *D) const {
232fe013be4SDimitry Andric     bool ShouldBeIncluded = true;
233fe013be4SDimitry Andric     // Check that we have the definition for redeclarable types.
234fe013be4SDimitry Andric     if (auto *TD = llvm::dyn_cast<TagDecl>(D))
235fe013be4SDimitry Andric       ShouldBeIncluded = TD->isThisDeclarationADefinition();
236fe013be4SDimitry Andric     else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
237fe013be4SDimitry Andric       ShouldBeIncluded = Interface->isThisDeclarationADefinition();
238fe013be4SDimitry Andric     else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
239fe013be4SDimitry Andric       ShouldBeIncluded = Protocol->isThisDeclarationADefinition();
240fe013be4SDimitry Andric 
241fe013be4SDimitry Andric     ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation());
242fe013be4SDimitry Andric     return ShouldBeIncluded;
243fe013be4SDimitry Andric   }
244fe013be4SDimitry Andric 
BatchExtractAPIVisitor__anon5832f3d50111::BatchExtractAPIVisitor245fe013be4SDimitry Andric   BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context,
246fe013be4SDimitry Andric                          APISet &API)
247fe013be4SDimitry Andric       : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {}
248fe013be4SDimitry Andric 
249fe013be4SDimitry Andric private:
250fe013be4SDimitry Andric   LocationFileChecker &LCF;
251fe013be4SDimitry Andric };
252fe013be4SDimitry Andric 
253fe013be4SDimitry Andric class WrappingExtractAPIConsumer : public ASTConsumer {
25481ad6265SDimitry Andric public:
WrappingExtractAPIConsumer(ASTContext & Context,APISet & API)255fe013be4SDimitry Andric   WrappingExtractAPIConsumer(ASTContext &Context, APISet &API)
256fe013be4SDimitry Andric       : Visitor(Context, API) {}
25781ad6265SDimitry Andric 
HandleTranslationUnit(ASTContext & Context)25881ad6265SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
25981ad6265SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
26081ad6265SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
26181ad6265SDimitry Andric   }
26281ad6265SDimitry Andric 
26381ad6265SDimitry Andric private:
264fe013be4SDimitry Andric   ExtractAPIVisitor<> Visitor;
265fe013be4SDimitry Andric };
266fe013be4SDimitry Andric 
267fe013be4SDimitry Andric class ExtractAPIConsumer : public ASTConsumer {
268fe013be4SDimitry Andric public:
ExtractAPIConsumer(ASTContext & Context,std::unique_ptr<LocationFileChecker> LCF,APISet & API)269fe013be4SDimitry Andric   ExtractAPIConsumer(ASTContext &Context,
270fe013be4SDimitry Andric                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
271fe013be4SDimitry Andric       : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {}
272fe013be4SDimitry Andric 
HandleTranslationUnit(ASTContext & Context)273fe013be4SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
274fe013be4SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
275fe013be4SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
276fe013be4SDimitry Andric   }
277fe013be4SDimitry Andric 
278fe013be4SDimitry Andric private:
279fe013be4SDimitry Andric   BatchExtractAPIVisitor Visitor;
28081ad6265SDimitry Andric   std::unique_ptr<LocationFileChecker> LCF;
28181ad6265SDimitry Andric };
28281ad6265SDimitry Andric 
28381ad6265SDimitry Andric class MacroCallback : public PPCallbacks {
28481ad6265SDimitry Andric public:
MacroCallback(const SourceManager & SM,APISet & API,Preprocessor & PP)285fe013be4SDimitry Andric   MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP)
286fe013be4SDimitry Andric       : SM(SM), API(API), PP(PP) {}
28781ad6265SDimitry Andric 
MacroDefined(const Token & MacroNameToken,const MacroDirective * MD)28881ad6265SDimitry Andric   void MacroDefined(const Token &MacroNameToken,
28981ad6265SDimitry Andric                     const MacroDirective *MD) override {
29081ad6265SDimitry Andric     auto *MacroInfo = MD->getMacroInfo();
29181ad6265SDimitry Andric 
29281ad6265SDimitry Andric     if (MacroInfo->isBuiltinMacro())
29381ad6265SDimitry Andric       return;
29481ad6265SDimitry Andric 
29581ad6265SDimitry Andric     auto SourceLoc = MacroNameToken.getLocation();
29681ad6265SDimitry Andric     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
29781ad6265SDimitry Andric         SM.isWrittenInCommandLineFile(SourceLoc))
29881ad6265SDimitry Andric       return;
29981ad6265SDimitry Andric 
30081ad6265SDimitry Andric     PendingMacros.emplace_back(MacroNameToken, MD);
30181ad6265SDimitry Andric   }
30281ad6265SDimitry Andric 
30381ad6265SDimitry Andric   // If a macro gets undefined at some point during preprocessing of the inputs
30481ad6265SDimitry Andric   // it means that it isn't an exposed API and we should therefore not add a
30581ad6265SDimitry Andric   // macro definition for it.
MacroUndefined(const Token & MacroNameToken,const MacroDefinition & MD,const MacroDirective * Undef)30681ad6265SDimitry Andric   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
30781ad6265SDimitry Andric                       const MacroDirective *Undef) override {
30881ad6265SDimitry Andric     // If this macro wasn't previously defined we don't need to do anything
30981ad6265SDimitry Andric     // here.
31081ad6265SDimitry Andric     if (!Undef)
31181ad6265SDimitry Andric       return;
31281ad6265SDimitry Andric 
31381ad6265SDimitry Andric     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
31481ad6265SDimitry Andric       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
31581ad6265SDimitry Andric                                               /*Syntactically*/ false);
31681ad6265SDimitry Andric     });
31781ad6265SDimitry Andric   }
31881ad6265SDimitry Andric 
EndOfMainFile()31981ad6265SDimitry Andric   void EndOfMainFile() override {
32081ad6265SDimitry Andric     for (auto &PM : PendingMacros) {
32181ad6265SDimitry Andric       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
32281ad6265SDimitry Andric       // file so check for it here.
32381ad6265SDimitry Andric       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
32481ad6265SDimitry Andric         continue;
32581ad6265SDimitry Andric 
326fe013be4SDimitry Andric       if (!shouldMacroBeIncluded(PM))
32781ad6265SDimitry Andric         continue;
32881ad6265SDimitry Andric 
32981ad6265SDimitry Andric       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
33081ad6265SDimitry Andric       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
33181ad6265SDimitry Andric       StringRef USR =
33281ad6265SDimitry Andric           API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
33381ad6265SDimitry Andric 
33481ad6265SDimitry Andric       API.addMacroDefinition(
33581ad6265SDimitry Andric           Name, USR, Loc,
33681ad6265SDimitry Andric           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
337bdd1243dSDimitry Andric           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
338bdd1243dSDimitry Andric           SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
33981ad6265SDimitry Andric     }
34081ad6265SDimitry Andric 
34181ad6265SDimitry Andric     PendingMacros.clear();
34281ad6265SDimitry Andric   }
34381ad6265SDimitry Andric 
344fe013be4SDimitry Andric protected:
34581ad6265SDimitry Andric   struct PendingMacro {
34681ad6265SDimitry Andric     Token MacroNameToken;
34781ad6265SDimitry Andric     const MacroDirective *MD;
34881ad6265SDimitry Andric 
PendingMacro__anon5832f3d50111::MacroCallback::PendingMacro34981ad6265SDimitry Andric     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
35081ad6265SDimitry Andric         : MacroNameToken(MacroNameToken), MD(MD) {}
35181ad6265SDimitry Andric   };
35281ad6265SDimitry Andric 
shouldMacroBeIncluded(const PendingMacro & PM)353fe013be4SDimitry Andric   virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; }
354fe013be4SDimitry Andric 
35581ad6265SDimitry Andric   const SourceManager &SM;
35681ad6265SDimitry Andric   APISet &API;
35781ad6265SDimitry Andric   Preprocessor &PP;
35881ad6265SDimitry Andric   llvm::SmallVector<PendingMacro> PendingMacros;
35981ad6265SDimitry Andric };
36081ad6265SDimitry Andric 
361fe013be4SDimitry Andric class APIMacroCallback : public MacroCallback {
362fe013be4SDimitry Andric public:
APIMacroCallback(const SourceManager & SM,APISet & API,Preprocessor & PP,LocationFileChecker & LCF)363fe013be4SDimitry Andric   APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP,
364fe013be4SDimitry Andric                    LocationFileChecker &LCF)
365fe013be4SDimitry Andric       : MacroCallback(SM, API, PP), LCF(LCF) {}
366fe013be4SDimitry Andric 
shouldMacroBeIncluded(const PendingMacro & PM)367fe013be4SDimitry Andric   bool shouldMacroBeIncluded(const PendingMacro &PM) override {
368fe013be4SDimitry Andric     // Do not include macros from external files
369fe013be4SDimitry Andric     return LCF(PM.MacroNameToken.getLocation());
370fe013be4SDimitry Andric   }
371fe013be4SDimitry Andric 
372fe013be4SDimitry Andric private:
373fe013be4SDimitry Andric   LocationFileChecker &LCF;
374fe013be4SDimitry Andric };
375fe013be4SDimitry Andric 
37681ad6265SDimitry Andric } // namespace
37781ad6265SDimitry Andric 
ImplEndSourceFileAction()378fe013be4SDimitry Andric void ExtractAPIActionBase::ImplEndSourceFileAction() {
379fe013be4SDimitry Andric   if (!OS)
380fe013be4SDimitry Andric     return;
381fe013be4SDimitry Andric 
382fe013be4SDimitry Andric   // Setup a SymbolGraphSerializer to write out collected API information in
383fe013be4SDimitry Andric   // the Symbol Graph format.
384fe013be4SDimitry Andric   // FIXME: Make the kind of APISerializer configurable.
385fe013be4SDimitry Andric   SymbolGraphSerializer SGSerializer(*API, IgnoresList);
386fe013be4SDimitry Andric   SGSerializer.serialize(*OS);
387fe013be4SDimitry Andric   OS.reset();
388fe013be4SDimitry Andric }
389fe013be4SDimitry Andric 
390fe013be4SDimitry Andric std::unique_ptr<raw_pwrite_stream>
CreateOutputFile(CompilerInstance & CI,StringRef InFile)391fe013be4SDimitry Andric ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
392fe013be4SDimitry Andric   std::unique_ptr<raw_pwrite_stream> OS;
393fe013be4SDimitry Andric   OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile,
394fe013be4SDimitry Andric                                   /*Extension=*/"json",
395fe013be4SDimitry Andric                                   /*RemoveFileOnSignal=*/false);
396fe013be4SDimitry Andric   if (!OS)
397fe013be4SDimitry Andric     return nullptr;
398fe013be4SDimitry Andric   return OS;
399fe013be4SDimitry Andric }
400fe013be4SDimitry Andric 
40181ad6265SDimitry Andric std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)40281ad6265SDimitry Andric ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
40381ad6265SDimitry Andric   OS = CreateOutputFile(CI, InFile);
404fe013be4SDimitry Andric 
40581ad6265SDimitry Andric   if (!OS)
40681ad6265SDimitry Andric     return nullptr;
40781ad6265SDimitry Andric 
408bdd1243dSDimitry Andric   auto ProductName = CI.getFrontendOpts().ProductName;
40981ad6265SDimitry Andric 
41081ad6265SDimitry Andric   // Now that we have enough information about the language options and the
41181ad6265SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
41281ad6265SDimitry Andric   API = std::make_unique<APISet>(
41381ad6265SDimitry Andric       CI.getTarget().getTriple(),
414bdd1243dSDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
41581ad6265SDimitry Andric 
41681ad6265SDimitry Andric   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
41781ad6265SDimitry Andric 
418fe013be4SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>(
419fe013be4SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF));
42081ad6265SDimitry Andric 
421bdd1243dSDimitry Andric   // Do not include location in anonymous decls.
422bdd1243dSDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
423bdd1243dSDimitry Andric   Policy.AnonymousTagLocations = false;
424bdd1243dSDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
425bdd1243dSDimitry Andric 
426fe013be4SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
427bdd1243dSDimitry Andric     llvm::handleAllErrors(
428fe013be4SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
429bdd1243dSDimitry Andric                                CI.getFileManager())
430bdd1243dSDimitry Andric             .moveInto(IgnoresList),
431bdd1243dSDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
432bdd1243dSDimitry Andric           CI.getDiagnostics().Report(
433bdd1243dSDimitry Andric               diag::err_extract_api_ignores_file_not_found)
434bdd1243dSDimitry Andric               << Err.Path;
435bdd1243dSDimitry Andric         });
436bdd1243dSDimitry Andric   }
437bdd1243dSDimitry Andric 
43881ad6265SDimitry Andric   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
43981ad6265SDimitry Andric                                               std::move(LCF), *API);
44081ad6265SDimitry Andric }
44181ad6265SDimitry Andric 
PrepareToExecuteAction(CompilerInstance & CI)44281ad6265SDimitry Andric bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
44381ad6265SDimitry Andric   auto &Inputs = CI.getFrontendOpts().Inputs;
44481ad6265SDimitry Andric   if (Inputs.empty())
44581ad6265SDimitry Andric     return true;
44681ad6265SDimitry Andric 
44781ad6265SDimitry Andric   if (!CI.hasFileManager())
44881ad6265SDimitry Andric     if (!CI.createFileManager())
44981ad6265SDimitry Andric       return false;
45081ad6265SDimitry Andric 
45181ad6265SDimitry Andric   auto Kind = Inputs[0].getKind();
45281ad6265SDimitry Andric 
45381ad6265SDimitry Andric   // Convert the header file inputs into a single input buffer.
45481ad6265SDimitry Andric   SmallString<256> HeaderContents;
45581ad6265SDimitry Andric   bool IsQuoted = false;
45681ad6265SDimitry Andric   for (const FrontendInputFile &FIF : Inputs) {
45781ad6265SDimitry Andric     if (Kind.isObjectiveC())
45881ad6265SDimitry Andric       HeaderContents += "#import";
45981ad6265SDimitry Andric     else
46081ad6265SDimitry Andric       HeaderContents += "#include";
46181ad6265SDimitry Andric 
46281ad6265SDimitry Andric     StringRef FilePath = FIF.getFile();
46381ad6265SDimitry Andric     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
46481ad6265SDimitry Andric       if (IsQuoted)
46581ad6265SDimitry Andric         HeaderContents += " \"";
46681ad6265SDimitry Andric       else
46781ad6265SDimitry Andric         HeaderContents += " <";
46881ad6265SDimitry Andric 
46981ad6265SDimitry Andric       HeaderContents += *RelativeName;
47081ad6265SDimitry Andric 
47181ad6265SDimitry Andric       if (IsQuoted)
47281ad6265SDimitry Andric         HeaderContents += "\"\n";
47381ad6265SDimitry Andric       else
47481ad6265SDimitry Andric         HeaderContents += ">\n";
47581ad6265SDimitry Andric       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
47681ad6265SDimitry Andric                                    IsQuoted);
47781ad6265SDimitry Andric     } else {
47881ad6265SDimitry Andric       HeaderContents += " \"";
47981ad6265SDimitry Andric       HeaderContents += FilePath;
48081ad6265SDimitry Andric       HeaderContents += "\"\n";
48181ad6265SDimitry Andric       KnownInputFiles.emplace_back(FilePath, true);
48281ad6265SDimitry Andric     }
48381ad6265SDimitry Andric   }
48481ad6265SDimitry Andric 
48581ad6265SDimitry Andric   if (CI.getHeaderSearchOpts().Verbose)
48681ad6265SDimitry Andric     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
48781ad6265SDimitry Andric                                 << HeaderContents << "\n";
48881ad6265SDimitry Andric 
48981ad6265SDimitry Andric   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
49081ad6265SDimitry Andric                                                 getInputBufferName());
49181ad6265SDimitry Andric 
49281ad6265SDimitry Andric   // Set that buffer up as our "real" input in the CompilerInstance.
49381ad6265SDimitry Andric   Inputs.clear();
49481ad6265SDimitry Andric   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
49581ad6265SDimitry Andric 
49681ad6265SDimitry Andric   return true;
49781ad6265SDimitry Andric }
49881ad6265SDimitry Andric 
EndSourceFileAction()499fe013be4SDimitry Andric void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); }
50081ad6265SDimitry Andric 
501fe013be4SDimitry Andric std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)502fe013be4SDimitry Andric WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
503fe013be4SDimitry Andric                                             StringRef InFile) {
504fe013be4SDimitry Andric   auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
505fe013be4SDimitry Andric   if (!OtherConsumer)
506fe013be4SDimitry Andric     return nullptr;
507fe013be4SDimitry Andric 
508fe013be4SDimitry Andric   CreatedASTConsumer = true;
509fe013be4SDimitry Andric 
510fe013be4SDimitry Andric   OS = CreateOutputFile(CI, InFile);
511fe013be4SDimitry Andric   if (!OS)
512fe013be4SDimitry Andric     return nullptr;
513fe013be4SDimitry Andric 
514fe013be4SDimitry Andric   auto ProductName = CI.getFrontendOpts().ProductName;
515fe013be4SDimitry Andric 
516fe013be4SDimitry Andric   // Now that we have enough information about the language options and the
517fe013be4SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
518fe013be4SDimitry Andric   API = std::make_unique<APISet>(
519fe013be4SDimitry Andric       CI.getTarget().getTriple(),
520fe013be4SDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
521fe013be4SDimitry Andric 
522fe013be4SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
523fe013be4SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor()));
524fe013be4SDimitry Andric 
525fe013be4SDimitry Andric   // Do not include location in anonymous decls.
526fe013be4SDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
527fe013be4SDimitry Andric   Policy.AnonymousTagLocations = false;
528fe013be4SDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
529fe013be4SDimitry Andric 
530fe013be4SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
531fe013be4SDimitry Andric     llvm::handleAllErrors(
532fe013be4SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
533fe013be4SDimitry Andric                                CI.getFileManager())
534fe013be4SDimitry Andric             .moveInto(IgnoresList),
535fe013be4SDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
536fe013be4SDimitry Andric           CI.getDiagnostics().Report(
537fe013be4SDimitry Andric               diag::err_extract_api_ignores_file_not_found)
538fe013be4SDimitry Andric               << Err.Path;
539fe013be4SDimitry Andric         });
540fe013be4SDimitry Andric   }
541fe013be4SDimitry Andric 
542fe013be4SDimitry Andric   auto WrappingConsumer =
543fe013be4SDimitry Andric       std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API);
544fe013be4SDimitry Andric   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
545fe013be4SDimitry Andric   Consumers.push_back(std::move(OtherConsumer));
546fe013be4SDimitry Andric   Consumers.push_back(std::move(WrappingConsumer));
547fe013be4SDimitry Andric 
548fe013be4SDimitry Andric   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
549fe013be4SDimitry Andric }
550fe013be4SDimitry Andric 
EndSourceFileAction()551fe013be4SDimitry Andric void WrappingExtractAPIAction::EndSourceFileAction() {
552fe013be4SDimitry Andric   // Invoke wrapped action's method.
553fe013be4SDimitry Andric   WrapperFrontendAction::EndSourceFileAction();
554fe013be4SDimitry Andric 
555fe013be4SDimitry Andric   if (CreatedASTConsumer) {
556fe013be4SDimitry Andric     ImplEndSourceFileAction();
557fe013be4SDimitry Andric   }
55881ad6265SDimitry Andric }
55981ad6265SDimitry Andric 
56081ad6265SDimitry Andric std::unique_ptr<raw_pwrite_stream>
CreateOutputFile(CompilerInstance & CI,StringRef InFile)561fe013be4SDimitry Andric WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI,
562fe013be4SDimitry Andric                                            StringRef InFile) {
563fe013be4SDimitry Andric   std::unique_ptr<raw_pwrite_stream> OS;
564fe013be4SDimitry Andric   std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir;
565fe013be4SDimitry Andric 
566fe013be4SDimitry Andric   // The symbol graphs need to be generated as a side effect of regular
567fe013be4SDimitry Andric   // compilation so the output should be dumped in the directory provided with
568fe013be4SDimitry Andric   // the command line option.
569fe013be4SDimitry Andric   llvm::SmallString<128> OutFilePath(OutputDir);
570fe013be4SDimitry Andric   auto Seperator = llvm::sys::path::get_separator();
571fe013be4SDimitry Andric   auto Infilename = llvm::sys::path::filename(InFile);
572fe013be4SDimitry Andric   OutFilePath.append({Seperator, Infilename});
573fe013be4SDimitry Andric   llvm::sys::path::replace_extension(OutFilePath, "json");
574fe013be4SDimitry Andric   // StringRef outputFilePathref = *OutFilePath;
575fe013be4SDimitry Andric 
576fe013be4SDimitry Andric   // don't use the default output file
577fe013be4SDimitry Andric   OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false,
578fe013be4SDimitry Andric                            /*RemoveFileOnSignal=*/true,
579fe013be4SDimitry Andric                            /*UseTemporary=*/true,
580fe013be4SDimitry Andric                            /*CreateMissingDirectories=*/true);
58181ad6265SDimitry Andric   if (!OS)
58281ad6265SDimitry Andric     return nullptr;
58381ad6265SDimitry Andric   return OS;
58481ad6265SDimitry Andric }
585