xref: /freebsd-src/contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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 
15*06c3fb27SDimitry Andric #include "clang/AST/ASTConcept.h"
1681ad6265SDimitry Andric #include "clang/AST/ASTConsumer.h"
1781ad6265SDimitry Andric #include "clang/AST/ASTContext.h"
18*06c3fb27SDimitry Andric #include "clang/AST/DeclObjC.h"
19bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticFrontend.h"
2081ad6265SDimitry Andric #include "clang/Basic/SourceLocation.h"
2181ad6265SDimitry Andric #include "clang/Basic/SourceManager.h"
2281ad6265SDimitry Andric #include "clang/Basic/TargetInfo.h"
2381ad6265SDimitry Andric #include "clang/ExtractAPI/API.h"
24bdd1243dSDimitry Andric #include "clang/ExtractAPI/APIIgnoresList.h"
25bdd1243dSDimitry Andric #include "clang/ExtractAPI/ExtractAPIVisitor.h"
2681ad6265SDimitry Andric #include "clang/ExtractAPI/FrontendActions.h"
2781ad6265SDimitry Andric #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
2881ad6265SDimitry Andric #include "clang/Frontend/ASTConsumers.h"
2981ad6265SDimitry Andric #include "clang/Frontend/CompilerInstance.h"
3081ad6265SDimitry Andric #include "clang/Frontend/FrontendOptions.h"
31*06c3fb27SDimitry Andric #include "clang/Frontend/MultiplexConsumer.h"
3281ad6265SDimitry Andric #include "clang/Lex/MacroInfo.h"
3381ad6265SDimitry Andric #include "clang/Lex/PPCallbacks.h"
3481ad6265SDimitry Andric #include "clang/Lex/Preprocessor.h"
3581ad6265SDimitry Andric #include "clang/Lex/PreprocessorOptions.h"
3681ad6265SDimitry Andric #include "llvm/ADT/DenseSet.h"
3781ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h"
38*06c3fb27SDimitry Andric #include "llvm/ADT/SmallString.h"
3981ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h"
40*06c3fb27SDimitry Andric #include "llvm/Support/Casting.h"
41bdd1243dSDimitry Andric #include "llvm/Support/Error.h"
4281ad6265SDimitry Andric #include "llvm/Support/FileSystem.h"
4381ad6265SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
4481ad6265SDimitry Andric #include "llvm/Support/Path.h"
4581ad6265SDimitry Andric #include "llvm/Support/Regex.h"
4681ad6265SDimitry Andric #include "llvm/Support/raw_ostream.h"
4781ad6265SDimitry Andric #include <memory>
48bdd1243dSDimitry Andric #include <optional>
4981ad6265SDimitry Andric #include <utility>
5081ad6265SDimitry Andric 
5181ad6265SDimitry Andric using namespace clang;
5281ad6265SDimitry Andric using namespace extractapi;
5381ad6265SDimitry Andric 
5481ad6265SDimitry Andric namespace {
5581ad6265SDimitry Andric 
56bdd1243dSDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
5781ad6265SDimitry Andric                                                   StringRef File,
5881ad6265SDimitry Andric                                                   bool *IsQuoted = nullptr) {
5981ad6265SDimitry Andric   assert(CI.hasFileManager() &&
6081ad6265SDimitry Andric          "CompilerInstance does not have a FileNamager!");
6181ad6265SDimitry Andric 
6281ad6265SDimitry Andric   using namespace llvm::sys;
6381ad6265SDimitry Andric   // Matches framework include patterns
6481ad6265SDimitry Andric   const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
6581ad6265SDimitry Andric 
6681ad6265SDimitry Andric   const auto &FS = CI.getVirtualFileSystem();
6781ad6265SDimitry Andric 
6881ad6265SDimitry Andric   SmallString<128> FilePath(File.begin(), File.end());
6981ad6265SDimitry Andric   FS.makeAbsolute(FilePath);
7081ad6265SDimitry Andric   path::remove_dots(FilePath, true);
7181ad6265SDimitry Andric   FilePath = path::convert_to_slash(FilePath);
7281ad6265SDimitry Andric   File = FilePath;
7381ad6265SDimitry Andric 
7481ad6265SDimitry Andric   // Checks whether `Dir` is a strict path prefix of `File`. If so returns
7581ad6265SDimitry Andric   // the prefix length. Otherwise return 0.
7681ad6265SDimitry Andric   auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
7781ad6265SDimitry Andric     llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
7881ad6265SDimitry Andric     FS.makeAbsolute(DirPath);
7981ad6265SDimitry Andric     path::remove_dots(DirPath, true);
8081ad6265SDimitry Andric     Dir = DirPath;
8181ad6265SDimitry Andric     for (auto NI = path::begin(File), NE = path::end(File),
8281ad6265SDimitry Andric               DI = path::begin(Dir), DE = path::end(Dir);
8381ad6265SDimitry Andric          /*termination condition in loop*/; ++NI, ++DI) {
8481ad6265SDimitry Andric       // '.' components in File are ignored.
8581ad6265SDimitry Andric       while (NI != NE && *NI == ".")
8681ad6265SDimitry Andric         ++NI;
8781ad6265SDimitry Andric       if (NI == NE)
8881ad6265SDimitry Andric         break;
8981ad6265SDimitry Andric 
9081ad6265SDimitry Andric       // '.' components in Dir are ignored.
9181ad6265SDimitry Andric       while (DI != DE && *DI == ".")
9281ad6265SDimitry Andric         ++DI;
9381ad6265SDimitry Andric 
9481ad6265SDimitry Andric       // Dir is a prefix of File, up to '.' components and choice of path
9581ad6265SDimitry Andric       // separators.
9681ad6265SDimitry Andric       if (DI == DE)
9781ad6265SDimitry Andric         return NI - path::begin(File);
9881ad6265SDimitry Andric 
9981ad6265SDimitry Andric       // Consider all path separators equal.
10081ad6265SDimitry Andric       if (NI->size() == 1 && DI->size() == 1 &&
10181ad6265SDimitry Andric           path::is_separator(NI->front()) && path::is_separator(DI->front()))
10281ad6265SDimitry Andric         continue;
10381ad6265SDimitry Andric 
10481ad6265SDimitry Andric       // Special case Apple .sdk folders since the search path is typically a
10581ad6265SDimitry Andric       // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
10681ad6265SDimitry Andric       // located in `iPhoneSimulator.sdk` (the real folder).
10781ad6265SDimitry Andric       if (NI->endswith(".sdk") && DI->endswith(".sdk")) {
10881ad6265SDimitry Andric         StringRef NBasename = path::stem(*NI);
10981ad6265SDimitry Andric         StringRef DBasename = path::stem(*DI);
11081ad6265SDimitry Andric         if (DBasename.startswith(NBasename))
11181ad6265SDimitry Andric           continue;
11281ad6265SDimitry Andric       }
11381ad6265SDimitry Andric 
11481ad6265SDimitry Andric       if (*NI != *DI)
11581ad6265SDimitry Andric         break;
11681ad6265SDimitry Andric     }
11781ad6265SDimitry Andric     return 0;
11881ad6265SDimitry Andric   };
11981ad6265SDimitry Andric 
12081ad6265SDimitry Andric   unsigned PrefixLength = 0;
12181ad6265SDimitry Andric 
12281ad6265SDimitry Andric   // Go through the search paths and find the first one that is a prefix of
12381ad6265SDimitry Andric   // the header.
12481ad6265SDimitry Andric   for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
12581ad6265SDimitry Andric     // Note whether the match is found in a quoted entry.
12681ad6265SDimitry Andric     if (IsQuoted)
12781ad6265SDimitry Andric       *IsQuoted = Entry.Group == frontend::Quoted;
12881ad6265SDimitry Andric 
12981ad6265SDimitry Andric     if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
13081ad6265SDimitry Andric       if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
13181ad6265SDimitry Andric         // If this is a headermap entry, try to reverse lookup the full path
13281ad6265SDimitry Andric         // for a spelled name before mapping.
13381ad6265SDimitry Andric         StringRef SpelledFilename = HMap->reverseLookupFilename(File);
13481ad6265SDimitry Andric         if (!SpelledFilename.empty())
13581ad6265SDimitry Andric           return SpelledFilename.str();
13681ad6265SDimitry Andric 
13781ad6265SDimitry Andric         // No matching mapping in this headermap, try next search entry.
13881ad6265SDimitry Andric         continue;
13981ad6265SDimitry Andric       }
14081ad6265SDimitry Andric     }
14181ad6265SDimitry Andric 
14281ad6265SDimitry Andric     // Entry is a directory search entry, try to check if it's a prefix of File.
14381ad6265SDimitry Andric     PrefixLength = CheckDir(Entry.Path);
14481ad6265SDimitry Andric     if (PrefixLength > 0) {
14581ad6265SDimitry Andric       // The header is found in a framework path, construct the framework-style
14681ad6265SDimitry Andric       // include name `<Framework/Header.h>`
14781ad6265SDimitry Andric       if (Entry.IsFramework) {
14881ad6265SDimitry Andric         SmallVector<StringRef, 4> Matches;
14981ad6265SDimitry Andric         Rule.match(File, &Matches);
15081ad6265SDimitry Andric         // Returned matches are always in stable order.
15181ad6265SDimitry Andric         if (Matches.size() != 4)
152bdd1243dSDimitry Andric           return std::nullopt;
15381ad6265SDimitry Andric 
15481ad6265SDimitry Andric         return path::convert_to_slash(
15581ad6265SDimitry Andric             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
15681ad6265SDimitry Andric              Matches[3])
15781ad6265SDimitry Andric                 .str());
15881ad6265SDimitry Andric       }
15981ad6265SDimitry Andric 
16081ad6265SDimitry Andric       // The header is found in a normal search path, strip the search path
16181ad6265SDimitry Andric       // prefix to get an include name.
16281ad6265SDimitry Andric       return path::convert_to_slash(File.drop_front(PrefixLength));
16381ad6265SDimitry Andric     }
16481ad6265SDimitry Andric   }
16581ad6265SDimitry Andric 
16681ad6265SDimitry Andric   // Couldn't determine a include name, use full path instead.
167bdd1243dSDimitry Andric   return std::nullopt;
16881ad6265SDimitry Andric }
16981ad6265SDimitry Andric 
17081ad6265SDimitry Andric struct LocationFileChecker {
171bdd1243dSDimitry Andric   bool operator()(SourceLocation Loc) {
17281ad6265SDimitry Andric     // If the loc refers to a macro expansion we need to first get the file
17381ad6265SDimitry Andric     // location of the expansion.
17481ad6265SDimitry Andric     auto &SM = CI.getSourceManager();
17581ad6265SDimitry Andric     auto FileLoc = SM.getFileLoc(Loc);
17681ad6265SDimitry Andric     FileID FID = SM.getFileID(FileLoc);
17781ad6265SDimitry Andric     if (FID.isInvalid())
17881ad6265SDimitry Andric       return false;
17981ad6265SDimitry Andric 
18081ad6265SDimitry Andric     const auto *File = SM.getFileEntryForID(FID);
18181ad6265SDimitry Andric     if (!File)
18281ad6265SDimitry Andric       return false;
18381ad6265SDimitry Andric 
18481ad6265SDimitry Andric     if (KnownFileEntries.count(File))
18581ad6265SDimitry Andric       return true;
18681ad6265SDimitry Andric 
18781ad6265SDimitry Andric     if (ExternalFileEntries.count(File))
18881ad6265SDimitry Andric       return false;
18981ad6265SDimitry Andric 
19081ad6265SDimitry Andric     StringRef FileName = File->tryGetRealPathName().empty()
19181ad6265SDimitry Andric                              ? File->getName()
19281ad6265SDimitry Andric                              : File->tryGetRealPathName();
19381ad6265SDimitry Andric 
19481ad6265SDimitry Andric     // Try to reduce the include name the same way we tried to include it.
19581ad6265SDimitry Andric     bool IsQuoted = false;
19681ad6265SDimitry Andric     if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
19781ad6265SDimitry Andric       if (llvm::any_of(KnownFiles,
19881ad6265SDimitry Andric                        [&IsQuoted, &IncludeName](const auto &KnownFile) {
19981ad6265SDimitry Andric                          return KnownFile.first.equals(*IncludeName) &&
20081ad6265SDimitry Andric                                 KnownFile.second == IsQuoted;
20181ad6265SDimitry Andric                        })) {
20281ad6265SDimitry Andric         KnownFileEntries.insert(File);
20381ad6265SDimitry Andric         return true;
20481ad6265SDimitry Andric       }
20581ad6265SDimitry Andric 
20681ad6265SDimitry Andric     // Record that the file was not found to avoid future reverse lookup for
20781ad6265SDimitry Andric     // the same file.
20881ad6265SDimitry Andric     ExternalFileEntries.insert(File);
20981ad6265SDimitry Andric     return false;
21081ad6265SDimitry Andric   }
21181ad6265SDimitry Andric 
21281ad6265SDimitry Andric   LocationFileChecker(const CompilerInstance &CI,
21381ad6265SDimitry Andric                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
21481ad6265SDimitry Andric       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
21581ad6265SDimitry Andric     for (const auto &KnownFile : KnownFiles)
21681ad6265SDimitry Andric       if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
21781ad6265SDimitry Andric         KnownFileEntries.insert(*FileEntry);
21881ad6265SDimitry Andric   }
21981ad6265SDimitry Andric 
22081ad6265SDimitry Andric private:
22181ad6265SDimitry Andric   const CompilerInstance &CI;
22281ad6265SDimitry Andric   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
22381ad6265SDimitry Andric   llvm::DenseSet<const FileEntry *> KnownFileEntries;
22481ad6265SDimitry Andric   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
22581ad6265SDimitry Andric };
22681ad6265SDimitry Andric 
227*06c3fb27SDimitry Andric struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
228*06c3fb27SDimitry Andric   bool shouldDeclBeIncluded(const Decl *D) const {
229*06c3fb27SDimitry Andric     bool ShouldBeIncluded = true;
230*06c3fb27SDimitry Andric     // Check that we have the definition for redeclarable types.
231*06c3fb27SDimitry Andric     if (auto *TD = llvm::dyn_cast<TagDecl>(D))
232*06c3fb27SDimitry Andric       ShouldBeIncluded = TD->isThisDeclarationADefinition();
233*06c3fb27SDimitry Andric     else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
234*06c3fb27SDimitry Andric       ShouldBeIncluded = Interface->isThisDeclarationADefinition();
235*06c3fb27SDimitry Andric     else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
236*06c3fb27SDimitry Andric       ShouldBeIncluded = Protocol->isThisDeclarationADefinition();
237*06c3fb27SDimitry Andric 
238*06c3fb27SDimitry Andric     ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation());
239*06c3fb27SDimitry Andric     return ShouldBeIncluded;
240*06c3fb27SDimitry Andric   }
241*06c3fb27SDimitry Andric 
242*06c3fb27SDimitry Andric   BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context,
243*06c3fb27SDimitry Andric                          APISet &API)
244*06c3fb27SDimitry Andric       : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {}
245*06c3fb27SDimitry Andric 
246*06c3fb27SDimitry Andric private:
247*06c3fb27SDimitry Andric   LocationFileChecker &LCF;
248*06c3fb27SDimitry Andric };
249*06c3fb27SDimitry Andric 
250*06c3fb27SDimitry Andric class WrappingExtractAPIConsumer : public ASTConsumer {
25181ad6265SDimitry Andric public:
252*06c3fb27SDimitry Andric   WrappingExtractAPIConsumer(ASTContext &Context, APISet &API)
253*06c3fb27SDimitry Andric       : Visitor(Context, API) {}
25481ad6265SDimitry Andric 
25581ad6265SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
25681ad6265SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
25781ad6265SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
25881ad6265SDimitry Andric   }
25981ad6265SDimitry Andric 
26081ad6265SDimitry Andric private:
261*06c3fb27SDimitry Andric   ExtractAPIVisitor<> Visitor;
262*06c3fb27SDimitry Andric };
263*06c3fb27SDimitry Andric 
264*06c3fb27SDimitry Andric class ExtractAPIConsumer : public ASTConsumer {
265*06c3fb27SDimitry Andric public:
266*06c3fb27SDimitry Andric   ExtractAPIConsumer(ASTContext &Context,
267*06c3fb27SDimitry Andric                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
268*06c3fb27SDimitry Andric       : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {}
269*06c3fb27SDimitry Andric 
270*06c3fb27SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
271*06c3fb27SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
272*06c3fb27SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
273*06c3fb27SDimitry Andric   }
274*06c3fb27SDimitry Andric 
275*06c3fb27SDimitry Andric private:
276*06c3fb27SDimitry Andric   BatchExtractAPIVisitor Visitor;
27781ad6265SDimitry Andric   std::unique_ptr<LocationFileChecker> LCF;
27881ad6265SDimitry Andric };
27981ad6265SDimitry Andric 
28081ad6265SDimitry Andric class MacroCallback : public PPCallbacks {
28181ad6265SDimitry Andric public:
282*06c3fb27SDimitry Andric   MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP)
283*06c3fb27SDimitry Andric       : SM(SM), API(API), PP(PP) {}
28481ad6265SDimitry Andric 
28581ad6265SDimitry Andric   void MacroDefined(const Token &MacroNameToken,
28681ad6265SDimitry Andric                     const MacroDirective *MD) override {
28781ad6265SDimitry Andric     auto *MacroInfo = MD->getMacroInfo();
28881ad6265SDimitry Andric 
28981ad6265SDimitry Andric     if (MacroInfo->isBuiltinMacro())
29081ad6265SDimitry Andric       return;
29181ad6265SDimitry Andric 
29281ad6265SDimitry Andric     auto SourceLoc = MacroNameToken.getLocation();
29381ad6265SDimitry Andric     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
29481ad6265SDimitry Andric         SM.isWrittenInCommandLineFile(SourceLoc))
29581ad6265SDimitry Andric       return;
29681ad6265SDimitry Andric 
29781ad6265SDimitry Andric     PendingMacros.emplace_back(MacroNameToken, MD);
29881ad6265SDimitry Andric   }
29981ad6265SDimitry Andric 
30081ad6265SDimitry Andric   // If a macro gets undefined at some point during preprocessing of the inputs
30181ad6265SDimitry Andric   // it means that it isn't an exposed API and we should therefore not add a
30281ad6265SDimitry Andric   // macro definition for it.
30381ad6265SDimitry Andric   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
30481ad6265SDimitry Andric                       const MacroDirective *Undef) override {
30581ad6265SDimitry Andric     // If this macro wasn't previously defined we don't need to do anything
30681ad6265SDimitry Andric     // here.
30781ad6265SDimitry Andric     if (!Undef)
30881ad6265SDimitry Andric       return;
30981ad6265SDimitry Andric 
31081ad6265SDimitry Andric     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
31181ad6265SDimitry Andric       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
31281ad6265SDimitry Andric                                               /*Syntactically*/ false);
31381ad6265SDimitry Andric     });
31481ad6265SDimitry Andric   }
31581ad6265SDimitry Andric 
31681ad6265SDimitry Andric   void EndOfMainFile() override {
31781ad6265SDimitry Andric     for (auto &PM : PendingMacros) {
31881ad6265SDimitry Andric       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
31981ad6265SDimitry Andric       // file so check for it here.
32081ad6265SDimitry Andric       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
32181ad6265SDimitry Andric         continue;
32281ad6265SDimitry Andric 
323*06c3fb27SDimitry Andric       if (!shouldMacroBeIncluded(PM))
32481ad6265SDimitry Andric         continue;
32581ad6265SDimitry Andric 
32681ad6265SDimitry Andric       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
32781ad6265SDimitry Andric       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
32881ad6265SDimitry Andric       StringRef USR =
32981ad6265SDimitry Andric           API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
33081ad6265SDimitry Andric 
33181ad6265SDimitry Andric       API.addMacroDefinition(
33281ad6265SDimitry Andric           Name, USR, Loc,
33381ad6265SDimitry Andric           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
334bdd1243dSDimitry Andric           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
335bdd1243dSDimitry Andric           SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
33681ad6265SDimitry Andric     }
33781ad6265SDimitry Andric 
33881ad6265SDimitry Andric     PendingMacros.clear();
33981ad6265SDimitry Andric   }
34081ad6265SDimitry Andric 
341*06c3fb27SDimitry Andric protected:
34281ad6265SDimitry Andric   struct PendingMacro {
34381ad6265SDimitry Andric     Token MacroNameToken;
34481ad6265SDimitry Andric     const MacroDirective *MD;
34581ad6265SDimitry Andric 
34681ad6265SDimitry Andric     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
34781ad6265SDimitry Andric         : MacroNameToken(MacroNameToken), MD(MD) {}
34881ad6265SDimitry Andric   };
34981ad6265SDimitry Andric 
350*06c3fb27SDimitry Andric   virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; }
351*06c3fb27SDimitry Andric 
35281ad6265SDimitry Andric   const SourceManager &SM;
35381ad6265SDimitry Andric   APISet &API;
35481ad6265SDimitry Andric   Preprocessor &PP;
35581ad6265SDimitry Andric   llvm::SmallVector<PendingMacro> PendingMacros;
35681ad6265SDimitry Andric };
35781ad6265SDimitry Andric 
358*06c3fb27SDimitry Andric class APIMacroCallback : public MacroCallback {
359*06c3fb27SDimitry Andric public:
360*06c3fb27SDimitry Andric   APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP,
361*06c3fb27SDimitry Andric                    LocationFileChecker &LCF)
362*06c3fb27SDimitry Andric       : MacroCallback(SM, API, PP), LCF(LCF) {}
363*06c3fb27SDimitry Andric 
364*06c3fb27SDimitry Andric   bool shouldMacroBeIncluded(const PendingMacro &PM) override {
365*06c3fb27SDimitry Andric     // Do not include macros from external files
366*06c3fb27SDimitry Andric     return LCF(PM.MacroNameToken.getLocation());
367*06c3fb27SDimitry Andric   }
368*06c3fb27SDimitry Andric 
369*06c3fb27SDimitry Andric private:
370*06c3fb27SDimitry Andric   LocationFileChecker &LCF;
371*06c3fb27SDimitry Andric };
372*06c3fb27SDimitry Andric 
37381ad6265SDimitry Andric } // namespace
37481ad6265SDimitry Andric 
375*06c3fb27SDimitry Andric void ExtractAPIActionBase::ImplEndSourceFileAction() {
376*06c3fb27SDimitry Andric   if (!OS)
377*06c3fb27SDimitry Andric     return;
378*06c3fb27SDimitry Andric 
379*06c3fb27SDimitry Andric   // Setup a SymbolGraphSerializer to write out collected API information in
380*06c3fb27SDimitry Andric   // the Symbol Graph format.
381*06c3fb27SDimitry Andric   // FIXME: Make the kind of APISerializer configurable.
382*06c3fb27SDimitry Andric   SymbolGraphSerializer SGSerializer(*API, IgnoresList);
383*06c3fb27SDimitry Andric   SGSerializer.serialize(*OS);
384*06c3fb27SDimitry Andric   OS.reset();
385*06c3fb27SDimitry Andric }
386*06c3fb27SDimitry Andric 
387*06c3fb27SDimitry Andric std::unique_ptr<raw_pwrite_stream>
388*06c3fb27SDimitry Andric ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
389*06c3fb27SDimitry Andric   std::unique_ptr<raw_pwrite_stream> OS;
390*06c3fb27SDimitry Andric   OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile,
391*06c3fb27SDimitry Andric                                   /*Extension=*/"json",
392*06c3fb27SDimitry Andric                                   /*RemoveFileOnSignal=*/false);
393*06c3fb27SDimitry Andric   if (!OS)
394*06c3fb27SDimitry Andric     return nullptr;
395*06c3fb27SDimitry Andric   return OS;
396*06c3fb27SDimitry Andric }
397*06c3fb27SDimitry Andric 
39881ad6265SDimitry Andric std::unique_ptr<ASTConsumer>
39981ad6265SDimitry Andric ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
40081ad6265SDimitry Andric   OS = CreateOutputFile(CI, InFile);
401*06c3fb27SDimitry Andric 
40281ad6265SDimitry Andric   if (!OS)
40381ad6265SDimitry Andric     return nullptr;
40481ad6265SDimitry Andric 
405bdd1243dSDimitry Andric   auto ProductName = CI.getFrontendOpts().ProductName;
40681ad6265SDimitry Andric 
40781ad6265SDimitry Andric   // Now that we have enough information about the language options and the
40881ad6265SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
40981ad6265SDimitry Andric   API = std::make_unique<APISet>(
41081ad6265SDimitry Andric       CI.getTarget().getTriple(),
411bdd1243dSDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
41281ad6265SDimitry Andric 
41381ad6265SDimitry Andric   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
41481ad6265SDimitry Andric 
415*06c3fb27SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>(
416*06c3fb27SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF));
41781ad6265SDimitry Andric 
418bdd1243dSDimitry Andric   // Do not include location in anonymous decls.
419bdd1243dSDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
420bdd1243dSDimitry Andric   Policy.AnonymousTagLocations = false;
421bdd1243dSDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
422bdd1243dSDimitry Andric 
423*06c3fb27SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
424bdd1243dSDimitry Andric     llvm::handleAllErrors(
425*06c3fb27SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
426bdd1243dSDimitry Andric                                CI.getFileManager())
427bdd1243dSDimitry Andric             .moveInto(IgnoresList),
428bdd1243dSDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
429bdd1243dSDimitry Andric           CI.getDiagnostics().Report(
430bdd1243dSDimitry Andric               diag::err_extract_api_ignores_file_not_found)
431bdd1243dSDimitry Andric               << Err.Path;
432bdd1243dSDimitry Andric         });
433bdd1243dSDimitry Andric   }
434bdd1243dSDimitry Andric 
43581ad6265SDimitry Andric   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
43681ad6265SDimitry Andric                                               std::move(LCF), *API);
43781ad6265SDimitry Andric }
43881ad6265SDimitry Andric 
43981ad6265SDimitry Andric bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
44081ad6265SDimitry Andric   auto &Inputs = CI.getFrontendOpts().Inputs;
44181ad6265SDimitry Andric   if (Inputs.empty())
44281ad6265SDimitry Andric     return true;
44381ad6265SDimitry Andric 
44481ad6265SDimitry Andric   if (!CI.hasFileManager())
44581ad6265SDimitry Andric     if (!CI.createFileManager())
44681ad6265SDimitry Andric       return false;
44781ad6265SDimitry Andric 
44881ad6265SDimitry Andric   auto Kind = Inputs[0].getKind();
44981ad6265SDimitry Andric 
45081ad6265SDimitry Andric   // Convert the header file inputs into a single input buffer.
45181ad6265SDimitry Andric   SmallString<256> HeaderContents;
45281ad6265SDimitry Andric   bool IsQuoted = false;
45381ad6265SDimitry Andric   for (const FrontendInputFile &FIF : Inputs) {
45481ad6265SDimitry Andric     if (Kind.isObjectiveC())
45581ad6265SDimitry Andric       HeaderContents += "#import";
45681ad6265SDimitry Andric     else
45781ad6265SDimitry Andric       HeaderContents += "#include";
45881ad6265SDimitry Andric 
45981ad6265SDimitry Andric     StringRef FilePath = FIF.getFile();
46081ad6265SDimitry Andric     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
46181ad6265SDimitry Andric       if (IsQuoted)
46281ad6265SDimitry Andric         HeaderContents += " \"";
46381ad6265SDimitry Andric       else
46481ad6265SDimitry Andric         HeaderContents += " <";
46581ad6265SDimitry Andric 
46681ad6265SDimitry Andric       HeaderContents += *RelativeName;
46781ad6265SDimitry Andric 
46881ad6265SDimitry Andric       if (IsQuoted)
46981ad6265SDimitry Andric         HeaderContents += "\"\n";
47081ad6265SDimitry Andric       else
47181ad6265SDimitry Andric         HeaderContents += ">\n";
47281ad6265SDimitry Andric       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
47381ad6265SDimitry Andric                                    IsQuoted);
47481ad6265SDimitry Andric     } else {
47581ad6265SDimitry Andric       HeaderContents += " \"";
47681ad6265SDimitry Andric       HeaderContents += FilePath;
47781ad6265SDimitry Andric       HeaderContents += "\"\n";
47881ad6265SDimitry Andric       KnownInputFiles.emplace_back(FilePath, true);
47981ad6265SDimitry Andric     }
48081ad6265SDimitry Andric   }
48181ad6265SDimitry Andric 
48281ad6265SDimitry Andric   if (CI.getHeaderSearchOpts().Verbose)
48381ad6265SDimitry Andric     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
48481ad6265SDimitry Andric                                 << HeaderContents << "\n";
48581ad6265SDimitry Andric 
48681ad6265SDimitry Andric   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
48781ad6265SDimitry Andric                                                 getInputBufferName());
48881ad6265SDimitry Andric 
48981ad6265SDimitry Andric   // Set that buffer up as our "real" input in the CompilerInstance.
49081ad6265SDimitry Andric   Inputs.clear();
49181ad6265SDimitry Andric   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
49281ad6265SDimitry Andric 
49381ad6265SDimitry Andric   return true;
49481ad6265SDimitry Andric }
49581ad6265SDimitry Andric 
496*06c3fb27SDimitry Andric void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); }
49781ad6265SDimitry Andric 
498*06c3fb27SDimitry Andric std::unique_ptr<ASTConsumer>
499*06c3fb27SDimitry Andric WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
500*06c3fb27SDimitry Andric                                             StringRef InFile) {
501*06c3fb27SDimitry Andric   auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
502*06c3fb27SDimitry Andric   if (!OtherConsumer)
503*06c3fb27SDimitry Andric     return nullptr;
504*06c3fb27SDimitry Andric 
505*06c3fb27SDimitry Andric   CreatedASTConsumer = true;
506*06c3fb27SDimitry Andric 
507*06c3fb27SDimitry Andric   OS = CreateOutputFile(CI, InFile);
508*06c3fb27SDimitry Andric   if (!OS)
509*06c3fb27SDimitry Andric     return nullptr;
510*06c3fb27SDimitry Andric 
511*06c3fb27SDimitry Andric   auto ProductName = CI.getFrontendOpts().ProductName;
512*06c3fb27SDimitry Andric 
513*06c3fb27SDimitry Andric   // Now that we have enough information about the language options and the
514*06c3fb27SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
515*06c3fb27SDimitry Andric   API = std::make_unique<APISet>(
516*06c3fb27SDimitry Andric       CI.getTarget().getTriple(),
517*06c3fb27SDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
518*06c3fb27SDimitry Andric 
519*06c3fb27SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
520*06c3fb27SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor()));
521*06c3fb27SDimitry Andric 
522*06c3fb27SDimitry Andric   // Do not include location in anonymous decls.
523*06c3fb27SDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
524*06c3fb27SDimitry Andric   Policy.AnonymousTagLocations = false;
525*06c3fb27SDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
526*06c3fb27SDimitry Andric 
527*06c3fb27SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
528*06c3fb27SDimitry Andric     llvm::handleAllErrors(
529*06c3fb27SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
530*06c3fb27SDimitry Andric                                CI.getFileManager())
531*06c3fb27SDimitry Andric             .moveInto(IgnoresList),
532*06c3fb27SDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
533*06c3fb27SDimitry Andric           CI.getDiagnostics().Report(
534*06c3fb27SDimitry Andric               diag::err_extract_api_ignores_file_not_found)
535*06c3fb27SDimitry Andric               << Err.Path;
536*06c3fb27SDimitry Andric         });
537*06c3fb27SDimitry Andric   }
538*06c3fb27SDimitry Andric 
539*06c3fb27SDimitry Andric   auto WrappingConsumer =
540*06c3fb27SDimitry Andric       std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API);
541*06c3fb27SDimitry Andric   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
542*06c3fb27SDimitry Andric   Consumers.push_back(std::move(OtherConsumer));
543*06c3fb27SDimitry Andric   Consumers.push_back(std::move(WrappingConsumer));
544*06c3fb27SDimitry Andric 
545*06c3fb27SDimitry Andric   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
546*06c3fb27SDimitry Andric }
547*06c3fb27SDimitry Andric 
548*06c3fb27SDimitry Andric void WrappingExtractAPIAction::EndSourceFileAction() {
549*06c3fb27SDimitry Andric   // Invoke wrapped action's method.
550*06c3fb27SDimitry Andric   WrapperFrontendAction::EndSourceFileAction();
551*06c3fb27SDimitry Andric 
552*06c3fb27SDimitry Andric   if (CreatedASTConsumer) {
553*06c3fb27SDimitry Andric     ImplEndSourceFileAction();
554*06c3fb27SDimitry Andric   }
55581ad6265SDimitry Andric }
55681ad6265SDimitry Andric 
55781ad6265SDimitry Andric std::unique_ptr<raw_pwrite_stream>
558*06c3fb27SDimitry Andric WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI,
559*06c3fb27SDimitry Andric                                            StringRef InFile) {
560*06c3fb27SDimitry Andric   std::unique_ptr<raw_pwrite_stream> OS;
561*06c3fb27SDimitry Andric   std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir;
562*06c3fb27SDimitry Andric 
563*06c3fb27SDimitry Andric   // The symbol graphs need to be generated as a side effect of regular
564*06c3fb27SDimitry Andric   // compilation so the output should be dumped in the directory provided with
565*06c3fb27SDimitry Andric   // the command line option.
566*06c3fb27SDimitry Andric   llvm::SmallString<128> OutFilePath(OutputDir);
567*06c3fb27SDimitry Andric   auto Seperator = llvm::sys::path::get_separator();
568*06c3fb27SDimitry Andric   auto Infilename = llvm::sys::path::filename(InFile);
569*06c3fb27SDimitry Andric   OutFilePath.append({Seperator, Infilename});
570*06c3fb27SDimitry Andric   llvm::sys::path::replace_extension(OutFilePath, "json");
571*06c3fb27SDimitry Andric   // StringRef outputFilePathref = *OutFilePath;
572*06c3fb27SDimitry Andric 
573*06c3fb27SDimitry Andric   // don't use the default output file
574*06c3fb27SDimitry Andric   OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false,
575*06c3fb27SDimitry Andric                            /*RemoveFileOnSignal=*/true,
576*06c3fb27SDimitry Andric                            /*UseTemporary=*/true,
577*06c3fb27SDimitry Andric                            /*CreateMissingDirectories=*/true);
57881ad6265SDimitry Andric   if (!OS)
57981ad6265SDimitry Andric     return nullptr;
58081ad6265SDimitry Andric   return OS;
58181ad6265SDimitry Andric }
582