xref: /freebsd/contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 
1506c3fb27SDimitry Andric #include "clang/AST/ASTConcept.h"
1681ad6265SDimitry Andric #include "clang/AST/ASTConsumer.h"
1781ad6265SDimitry Andric #include "clang/AST/ASTContext.h"
1806c3fb27SDimitry Andric #include "clang/AST/DeclObjC.h"
19bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticFrontend.h"
205f757f3fSDimitry 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"
3206c3fb27SDimitry Andric #include "clang/Frontend/MultiplexConsumer.h"
33*0fca6ea1SDimitry Andric #include "clang/Index/USRGeneration.h"
34*0fca6ea1SDimitry Andric #include "clang/InstallAPI/HeaderFile.h"
3581ad6265SDimitry Andric #include "clang/Lex/MacroInfo.h"
3681ad6265SDimitry Andric #include "clang/Lex/PPCallbacks.h"
3781ad6265SDimitry Andric #include "clang/Lex/Preprocessor.h"
3881ad6265SDimitry Andric #include "clang/Lex/PreprocessorOptions.h"
3981ad6265SDimitry Andric #include "llvm/ADT/DenseSet.h"
4081ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h"
4106c3fb27SDimitry Andric #include "llvm/ADT/SmallString.h"
4281ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h"
43*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h"
4406c3fb27SDimitry Andric #include "llvm/Support/Casting.h"
45bdd1243dSDimitry Andric #include "llvm/Support/Error.h"
4681ad6265SDimitry Andric #include "llvm/Support/FileSystem.h"
4781ad6265SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
4881ad6265SDimitry Andric #include "llvm/Support/Path.h"
4981ad6265SDimitry Andric #include "llvm/Support/Regex.h"
5081ad6265SDimitry Andric #include "llvm/Support/raw_ostream.h"
5181ad6265SDimitry Andric #include <memory>
52bdd1243dSDimitry Andric #include <optional>
5381ad6265SDimitry Andric #include <utility>
5481ad6265SDimitry Andric 
5581ad6265SDimitry Andric using namespace clang;
5681ad6265SDimitry Andric using namespace extractapi;
5781ad6265SDimitry Andric 
5881ad6265SDimitry Andric namespace {
5981ad6265SDimitry Andric 
getRelativeIncludeName(const CompilerInstance & CI,StringRef File,bool * IsQuoted=nullptr)60bdd1243dSDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
6181ad6265SDimitry Andric                                                   StringRef File,
6281ad6265SDimitry Andric                                                   bool *IsQuoted = nullptr) {
6381ad6265SDimitry Andric   assert(CI.hasFileManager() &&
6481ad6265SDimitry Andric          "CompilerInstance does not have a FileNamager!");
6581ad6265SDimitry Andric 
6681ad6265SDimitry Andric   using namespace llvm::sys;
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).
1085f757f3fSDimitry Andric       if (NI->ends_with(".sdk") && DI->ends_with(".sdk")) {
10981ad6265SDimitry Andric         StringRef NBasename = path::stem(*NI);
11081ad6265SDimitry Andric         StringRef DBasename = path::stem(*DI);
1115f757f3fSDimitry 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;
150*0fca6ea1SDimitry Andric         clang::installapi::HeaderFile::getFrameworkIncludeRule().match(
151*0fca6ea1SDimitry Andric             File, &Matches);
15281ad6265SDimitry Andric         // Returned matches are always in stable order.
15381ad6265SDimitry Andric         if (Matches.size() != 4)
154bdd1243dSDimitry Andric           return std::nullopt;
15581ad6265SDimitry Andric 
15681ad6265SDimitry Andric         return path::convert_to_slash(
15781ad6265SDimitry Andric             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
15881ad6265SDimitry Andric              Matches[3])
15981ad6265SDimitry Andric                 .str());
16081ad6265SDimitry Andric       }
16181ad6265SDimitry Andric 
16281ad6265SDimitry Andric       // The header is found in a normal search path, strip the search path
16381ad6265SDimitry Andric       // prefix to get an include name.
16481ad6265SDimitry Andric       return path::convert_to_slash(File.drop_front(PrefixLength));
16581ad6265SDimitry Andric     }
16681ad6265SDimitry Andric   }
16781ad6265SDimitry Andric 
16881ad6265SDimitry Andric   // Couldn't determine a include name, use full path instead.
169bdd1243dSDimitry Andric   return std::nullopt;
17081ad6265SDimitry Andric }
17181ad6265SDimitry Andric 
getRelativeIncludeName(const CompilerInstance & CI,FileEntryRef FE,bool * IsQuoted=nullptr)1725f757f3fSDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
1735f757f3fSDimitry Andric                                                   FileEntryRef FE,
1745f757f3fSDimitry Andric                                                   bool *IsQuoted = nullptr) {
1755f757f3fSDimitry Andric   return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted);
1765f757f3fSDimitry Andric }
1775f757f3fSDimitry Andric 
17881ad6265SDimitry Andric struct LocationFileChecker {
operator ()__anon9569e29f0111::LocationFileChecker179bdd1243dSDimitry Andric   bool operator()(SourceLocation Loc) {
18081ad6265SDimitry Andric     // If the loc refers to a macro expansion we need to first get the file
18181ad6265SDimitry Andric     // location of the expansion.
18281ad6265SDimitry Andric     auto &SM = CI.getSourceManager();
18381ad6265SDimitry Andric     auto FileLoc = SM.getFileLoc(Loc);
18481ad6265SDimitry Andric     FileID FID = SM.getFileID(FileLoc);
18581ad6265SDimitry Andric     if (FID.isInvalid())
18681ad6265SDimitry Andric       return false;
18781ad6265SDimitry Andric 
1885f757f3fSDimitry Andric     OptionalFileEntryRef File = SM.getFileEntryRefForID(FID);
18981ad6265SDimitry Andric     if (!File)
19081ad6265SDimitry Andric       return false;
19181ad6265SDimitry Andric 
1925f757f3fSDimitry Andric     if (KnownFileEntries.count(*File))
19381ad6265SDimitry Andric       return true;
19481ad6265SDimitry Andric 
1955f757f3fSDimitry Andric     if (ExternalFileEntries.count(*File))
19681ad6265SDimitry Andric       return false;
19781ad6265SDimitry Andric 
19881ad6265SDimitry Andric     // Try to reduce the include name the same way we tried to include it.
19981ad6265SDimitry Andric     bool IsQuoted = false;
2005f757f3fSDimitry Andric     if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted))
20181ad6265SDimitry Andric       if (llvm::any_of(KnownFiles,
20281ad6265SDimitry Andric                        [&IsQuoted, &IncludeName](const auto &KnownFile) {
20381ad6265SDimitry Andric                          return KnownFile.first.equals(*IncludeName) &&
20481ad6265SDimitry Andric                                 KnownFile.second == IsQuoted;
20581ad6265SDimitry Andric                        })) {
2065f757f3fSDimitry Andric         KnownFileEntries.insert(*File);
20781ad6265SDimitry Andric         return true;
20881ad6265SDimitry Andric       }
20981ad6265SDimitry Andric 
21081ad6265SDimitry Andric     // Record that the file was not found to avoid future reverse lookup for
21181ad6265SDimitry Andric     // the same file.
2125f757f3fSDimitry Andric     ExternalFileEntries.insert(*File);
21381ad6265SDimitry Andric     return false;
21481ad6265SDimitry Andric   }
21581ad6265SDimitry Andric 
LocationFileChecker__anon9569e29f0111::LocationFileChecker21681ad6265SDimitry Andric   LocationFileChecker(const CompilerInstance &CI,
21781ad6265SDimitry Andric                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
21881ad6265SDimitry Andric       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
21981ad6265SDimitry Andric     for (const auto &KnownFile : KnownFiles)
22081ad6265SDimitry Andric       if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
22181ad6265SDimitry Andric         KnownFileEntries.insert(*FileEntry);
22281ad6265SDimitry Andric   }
22381ad6265SDimitry Andric 
22481ad6265SDimitry Andric private:
22581ad6265SDimitry Andric   const CompilerInstance &CI;
22681ad6265SDimitry Andric   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
22781ad6265SDimitry Andric   llvm::DenseSet<const FileEntry *> KnownFileEntries;
22881ad6265SDimitry Andric   llvm::DenseSet<const FileEntry *> ExternalFileEntries;
22981ad6265SDimitry Andric };
23081ad6265SDimitry Andric 
23106c3fb27SDimitry Andric struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> {
shouldDeclBeIncluded__anon9569e29f0111::BatchExtractAPIVisitor23206c3fb27SDimitry Andric   bool shouldDeclBeIncluded(const Decl *D) const {
23306c3fb27SDimitry Andric     bool ShouldBeIncluded = true;
23406c3fb27SDimitry Andric     // Check that we have the definition for redeclarable types.
23506c3fb27SDimitry Andric     if (auto *TD = llvm::dyn_cast<TagDecl>(D))
23606c3fb27SDimitry Andric       ShouldBeIncluded = TD->isThisDeclarationADefinition();
23706c3fb27SDimitry Andric     else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D))
23806c3fb27SDimitry Andric       ShouldBeIncluded = Interface->isThisDeclarationADefinition();
23906c3fb27SDimitry Andric     else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D))
24006c3fb27SDimitry Andric       ShouldBeIncluded = Protocol->isThisDeclarationADefinition();
24106c3fb27SDimitry Andric 
24206c3fb27SDimitry Andric     ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation());
24306c3fb27SDimitry Andric     return ShouldBeIncluded;
24406c3fb27SDimitry Andric   }
24506c3fb27SDimitry Andric 
BatchExtractAPIVisitor__anon9569e29f0111::BatchExtractAPIVisitor24606c3fb27SDimitry Andric   BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context,
24706c3fb27SDimitry Andric                          APISet &API)
24806c3fb27SDimitry Andric       : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {}
24906c3fb27SDimitry Andric 
25006c3fb27SDimitry Andric private:
25106c3fb27SDimitry Andric   LocationFileChecker &LCF;
25206c3fb27SDimitry Andric };
25306c3fb27SDimitry Andric 
25406c3fb27SDimitry Andric class WrappingExtractAPIConsumer : public ASTConsumer {
25581ad6265SDimitry Andric public:
WrappingExtractAPIConsumer(ASTContext & Context,APISet & API)25606c3fb27SDimitry Andric   WrappingExtractAPIConsumer(ASTContext &Context, APISet &API)
25706c3fb27SDimitry Andric       : Visitor(Context, API) {}
25881ad6265SDimitry Andric 
HandleTranslationUnit(ASTContext & Context)25981ad6265SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
26081ad6265SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
26181ad6265SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
26281ad6265SDimitry Andric   }
26381ad6265SDimitry Andric 
26481ad6265SDimitry Andric private:
26506c3fb27SDimitry Andric   ExtractAPIVisitor<> Visitor;
26606c3fb27SDimitry Andric };
26706c3fb27SDimitry Andric 
26806c3fb27SDimitry Andric class ExtractAPIConsumer : public ASTConsumer {
26906c3fb27SDimitry Andric public:
ExtractAPIConsumer(ASTContext & Context,std::unique_ptr<LocationFileChecker> LCF,APISet & API)27006c3fb27SDimitry Andric   ExtractAPIConsumer(ASTContext &Context,
27106c3fb27SDimitry Andric                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
27206c3fb27SDimitry Andric       : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {}
27306c3fb27SDimitry Andric 
HandleTranslationUnit(ASTContext & Context)27406c3fb27SDimitry Andric   void HandleTranslationUnit(ASTContext &Context) override {
27506c3fb27SDimitry Andric     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
27606c3fb27SDimitry Andric     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
27706c3fb27SDimitry Andric   }
27806c3fb27SDimitry Andric 
27906c3fb27SDimitry Andric private:
28006c3fb27SDimitry Andric   BatchExtractAPIVisitor Visitor;
28181ad6265SDimitry Andric   std::unique_ptr<LocationFileChecker> LCF;
28281ad6265SDimitry Andric };
28381ad6265SDimitry Andric 
28481ad6265SDimitry Andric class MacroCallback : public PPCallbacks {
28581ad6265SDimitry Andric public:
MacroCallback(const SourceManager & SM,APISet & API,Preprocessor & PP)28606c3fb27SDimitry Andric   MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP)
28706c3fb27SDimitry Andric       : SM(SM), API(API), PP(PP) {}
28881ad6265SDimitry Andric 
MacroDefined(const Token & MacroNameToken,const MacroDirective * MD)28981ad6265SDimitry Andric   void MacroDefined(const Token &MacroNameToken,
29081ad6265SDimitry Andric                     const MacroDirective *MD) override {
29181ad6265SDimitry Andric     auto *MacroInfo = MD->getMacroInfo();
29281ad6265SDimitry Andric 
29381ad6265SDimitry Andric     if (MacroInfo->isBuiltinMacro())
29481ad6265SDimitry Andric       return;
29581ad6265SDimitry Andric 
29681ad6265SDimitry Andric     auto SourceLoc = MacroNameToken.getLocation();
29781ad6265SDimitry Andric     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
29881ad6265SDimitry Andric         SM.isWrittenInCommandLineFile(SourceLoc))
29981ad6265SDimitry Andric       return;
30081ad6265SDimitry Andric 
30181ad6265SDimitry Andric     PendingMacros.emplace_back(MacroNameToken, MD);
30281ad6265SDimitry Andric   }
30381ad6265SDimitry Andric 
30481ad6265SDimitry Andric   // If a macro gets undefined at some point during preprocessing of the inputs
30581ad6265SDimitry Andric   // it means that it isn't an exposed API and we should therefore not add a
30681ad6265SDimitry Andric   // macro definition for it.
MacroUndefined(const Token & MacroNameToken,const MacroDefinition & MD,const MacroDirective * Undef)30781ad6265SDimitry Andric   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
30881ad6265SDimitry Andric                       const MacroDirective *Undef) override {
30981ad6265SDimitry Andric     // If this macro wasn't previously defined we don't need to do anything
31081ad6265SDimitry Andric     // here.
31181ad6265SDimitry Andric     if (!Undef)
31281ad6265SDimitry Andric       return;
31381ad6265SDimitry Andric 
31481ad6265SDimitry Andric     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
31581ad6265SDimitry Andric       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
31681ad6265SDimitry Andric                                               /*Syntactically*/ false);
31781ad6265SDimitry Andric     });
31881ad6265SDimitry Andric   }
31981ad6265SDimitry Andric 
EndOfMainFile()32081ad6265SDimitry Andric   void EndOfMainFile() override {
32181ad6265SDimitry Andric     for (auto &PM : PendingMacros) {
32281ad6265SDimitry Andric       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
32381ad6265SDimitry Andric       // file so check for it here.
32481ad6265SDimitry Andric       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
32581ad6265SDimitry Andric         continue;
32681ad6265SDimitry Andric 
32706c3fb27SDimitry Andric       if (!shouldMacroBeIncluded(PM))
32881ad6265SDimitry Andric         continue;
32981ad6265SDimitry Andric 
33081ad6265SDimitry Andric       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
33181ad6265SDimitry Andric       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
332*0fca6ea1SDimitry Andric       SmallString<128> USR;
333*0fca6ea1SDimitry Andric       index::generateUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM,
334*0fca6ea1SDimitry Andric                                  USR);
33581ad6265SDimitry Andric 
336*0fca6ea1SDimitry Andric       API.createRecord<extractapi::MacroDefinitionRecord>(
337*0fca6ea1SDimitry Andric           USR, Name, SymbolReference(), Loc,
33881ad6265SDimitry Andric           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
339bdd1243dSDimitry Andric           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
340bdd1243dSDimitry Andric           SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
34181ad6265SDimitry Andric     }
34281ad6265SDimitry Andric 
34381ad6265SDimitry Andric     PendingMacros.clear();
34481ad6265SDimitry Andric   }
34581ad6265SDimitry Andric 
34606c3fb27SDimitry Andric protected:
34781ad6265SDimitry Andric   struct PendingMacro {
34881ad6265SDimitry Andric     Token MacroNameToken;
34981ad6265SDimitry Andric     const MacroDirective *MD;
35081ad6265SDimitry Andric 
PendingMacro__anon9569e29f0111::MacroCallback::PendingMacro35181ad6265SDimitry Andric     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
35281ad6265SDimitry Andric         : MacroNameToken(MacroNameToken), MD(MD) {}
35381ad6265SDimitry Andric   };
35481ad6265SDimitry Andric 
shouldMacroBeIncluded(const PendingMacro & PM)35506c3fb27SDimitry Andric   virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; }
35606c3fb27SDimitry Andric 
35781ad6265SDimitry Andric   const SourceManager &SM;
35881ad6265SDimitry Andric   APISet &API;
35981ad6265SDimitry Andric   Preprocessor &PP;
36081ad6265SDimitry Andric   llvm::SmallVector<PendingMacro> PendingMacros;
36181ad6265SDimitry Andric };
36281ad6265SDimitry Andric 
36306c3fb27SDimitry Andric class APIMacroCallback : public MacroCallback {
36406c3fb27SDimitry Andric public:
APIMacroCallback(const SourceManager & SM,APISet & API,Preprocessor & PP,LocationFileChecker & LCF)36506c3fb27SDimitry Andric   APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP,
36606c3fb27SDimitry Andric                    LocationFileChecker &LCF)
36706c3fb27SDimitry Andric       : MacroCallback(SM, API, PP), LCF(LCF) {}
36806c3fb27SDimitry Andric 
shouldMacroBeIncluded(const PendingMacro & PM)36906c3fb27SDimitry Andric   bool shouldMacroBeIncluded(const PendingMacro &PM) override {
37006c3fb27SDimitry Andric     // Do not include macros from external files
37106c3fb27SDimitry Andric     return LCF(PM.MacroNameToken.getLocation());
37206c3fb27SDimitry Andric   }
37306c3fb27SDimitry Andric 
37406c3fb27SDimitry Andric private:
37506c3fb27SDimitry Andric   LocationFileChecker &LCF;
37606c3fb27SDimitry Andric };
37706c3fb27SDimitry Andric 
378*0fca6ea1SDimitry Andric std::unique_ptr<llvm::raw_pwrite_stream>
createAdditionalSymbolGraphFile(CompilerInstance & CI,Twine BaseName)379*0fca6ea1SDimitry Andric createAdditionalSymbolGraphFile(CompilerInstance &CI, Twine BaseName) {
380*0fca6ea1SDimitry Andric   auto OutputDirectory = CI.getFrontendOpts().SymbolGraphOutputDir;
38181ad6265SDimitry Andric 
382*0fca6ea1SDimitry Andric   SmallString<256> FileName;
383*0fca6ea1SDimitry Andric   llvm::sys::path::append(FileName, OutputDirectory,
384*0fca6ea1SDimitry Andric                           BaseName + ".symbols.json");
385*0fca6ea1SDimitry Andric   return CI.createOutputFile(
386*0fca6ea1SDimitry Andric       FileName, /*Binary*/ false, /*RemoveFileOnSignal*/ false,
387*0fca6ea1SDimitry Andric       /*UseTemporary*/ true, /*CreateMissingDirectories*/ true);
38806c3fb27SDimitry Andric }
38906c3fb27SDimitry Andric 
390*0fca6ea1SDimitry Andric } // namespace
391*0fca6ea1SDimitry Andric 
ImplEndSourceFileAction(CompilerInstance & CI)392*0fca6ea1SDimitry Andric void ExtractAPIActionBase::ImplEndSourceFileAction(CompilerInstance &CI) {
393*0fca6ea1SDimitry Andric   SymbolGraphSerializerOption SerializationOptions;
394*0fca6ea1SDimitry Andric   SerializationOptions.Compact = !CI.getFrontendOpts().EmitPrettySymbolGraphs;
395*0fca6ea1SDimitry Andric   SerializationOptions.EmitSymbolLabelsForTesting =
396*0fca6ea1SDimitry Andric       CI.getFrontendOpts().EmitSymbolGraphSymbolLabelsForTesting;
397*0fca6ea1SDimitry Andric 
398*0fca6ea1SDimitry Andric   if (CI.getFrontendOpts().EmitExtensionSymbolGraphs) {
399*0fca6ea1SDimitry Andric     auto ConstructOutputFile = [&CI](Twine BaseName) {
400*0fca6ea1SDimitry Andric       return createAdditionalSymbolGraphFile(CI, BaseName);
401*0fca6ea1SDimitry Andric     };
402*0fca6ea1SDimitry Andric 
403*0fca6ea1SDimitry Andric     SymbolGraphSerializer::serializeWithExtensionGraphs(
404*0fca6ea1SDimitry Andric         *OS, *API, IgnoresList, ConstructOutputFile, SerializationOptions);
405*0fca6ea1SDimitry Andric   } else {
406*0fca6ea1SDimitry Andric     SymbolGraphSerializer::serializeMainSymbolGraph(*OS, *API, IgnoresList,
407*0fca6ea1SDimitry Andric                                                     SerializationOptions);
408*0fca6ea1SDimitry Andric   }
409*0fca6ea1SDimitry Andric 
410*0fca6ea1SDimitry Andric   // Flush the stream and close the main output stream.
411*0fca6ea1SDimitry Andric   OS.reset();
41206c3fb27SDimitry Andric }
41306c3fb27SDimitry Andric 
41481ad6265SDimitry Andric std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)41581ad6265SDimitry Andric ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
416*0fca6ea1SDimitry Andric   auto ProductName = CI.getFrontendOpts().ProductName;
417*0fca6ea1SDimitry Andric 
418*0fca6ea1SDimitry Andric   if (CI.getFrontendOpts().SymbolGraphOutputDir.empty())
419*0fca6ea1SDimitry Andric     OS = CI.createDefaultOutputFile(/*Binary*/ false, InFile,
420*0fca6ea1SDimitry Andric                                     /*Extension*/ "symbols.json",
421*0fca6ea1SDimitry Andric                                     /*RemoveFileOnSignal*/ false,
422*0fca6ea1SDimitry Andric                                     /*CreateMissingDirectories*/ true);
423*0fca6ea1SDimitry Andric   else
424*0fca6ea1SDimitry Andric     OS = createAdditionalSymbolGraphFile(CI, ProductName);
42506c3fb27SDimitry Andric 
42681ad6265SDimitry Andric   if (!OS)
42781ad6265SDimitry Andric     return nullptr;
42881ad6265SDimitry Andric 
42981ad6265SDimitry Andric   // Now that we have enough information about the language options and the
43081ad6265SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
43181ad6265SDimitry Andric   API = std::make_unique<APISet>(
43281ad6265SDimitry Andric       CI.getTarget().getTriple(),
433bdd1243dSDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
43481ad6265SDimitry Andric 
43581ad6265SDimitry Andric   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
43681ad6265SDimitry Andric 
43706c3fb27SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>(
43806c3fb27SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF));
43981ad6265SDimitry Andric 
440bdd1243dSDimitry Andric   // Do not include location in anonymous decls.
441bdd1243dSDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
442bdd1243dSDimitry Andric   Policy.AnonymousTagLocations = false;
443bdd1243dSDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
444bdd1243dSDimitry Andric 
44506c3fb27SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
446bdd1243dSDimitry Andric     llvm::handleAllErrors(
44706c3fb27SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
448bdd1243dSDimitry Andric                                CI.getFileManager())
449bdd1243dSDimitry Andric             .moveInto(IgnoresList),
450bdd1243dSDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
451bdd1243dSDimitry Andric           CI.getDiagnostics().Report(
452bdd1243dSDimitry Andric               diag::err_extract_api_ignores_file_not_found)
453bdd1243dSDimitry Andric               << Err.Path;
454bdd1243dSDimitry Andric         });
455bdd1243dSDimitry Andric   }
456bdd1243dSDimitry Andric 
45781ad6265SDimitry Andric   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
45881ad6265SDimitry Andric                                               std::move(LCF), *API);
45981ad6265SDimitry Andric }
46081ad6265SDimitry Andric 
PrepareToExecuteAction(CompilerInstance & CI)46181ad6265SDimitry Andric bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
46281ad6265SDimitry Andric   auto &Inputs = CI.getFrontendOpts().Inputs;
46381ad6265SDimitry Andric   if (Inputs.empty())
46481ad6265SDimitry Andric     return true;
46581ad6265SDimitry Andric 
46681ad6265SDimitry Andric   if (!CI.hasFileManager())
46781ad6265SDimitry Andric     if (!CI.createFileManager())
46881ad6265SDimitry Andric       return false;
46981ad6265SDimitry Andric 
47081ad6265SDimitry Andric   auto Kind = Inputs[0].getKind();
47181ad6265SDimitry Andric 
47281ad6265SDimitry Andric   // Convert the header file inputs into a single input buffer.
47381ad6265SDimitry Andric   SmallString<256> HeaderContents;
47481ad6265SDimitry Andric   bool IsQuoted = false;
47581ad6265SDimitry Andric   for (const FrontendInputFile &FIF : Inputs) {
47681ad6265SDimitry Andric     if (Kind.isObjectiveC())
47781ad6265SDimitry Andric       HeaderContents += "#import";
47881ad6265SDimitry Andric     else
47981ad6265SDimitry Andric       HeaderContents += "#include";
48081ad6265SDimitry Andric 
48181ad6265SDimitry Andric     StringRef FilePath = FIF.getFile();
48281ad6265SDimitry Andric     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
48381ad6265SDimitry Andric       if (IsQuoted)
48481ad6265SDimitry Andric         HeaderContents += " \"";
48581ad6265SDimitry Andric       else
48681ad6265SDimitry Andric         HeaderContents += " <";
48781ad6265SDimitry Andric 
48881ad6265SDimitry Andric       HeaderContents += *RelativeName;
48981ad6265SDimitry Andric 
49081ad6265SDimitry Andric       if (IsQuoted)
49181ad6265SDimitry Andric         HeaderContents += "\"\n";
49281ad6265SDimitry Andric       else
49381ad6265SDimitry Andric         HeaderContents += ">\n";
49481ad6265SDimitry Andric       KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
49581ad6265SDimitry Andric                                    IsQuoted);
49681ad6265SDimitry Andric     } else {
49781ad6265SDimitry Andric       HeaderContents += " \"";
49881ad6265SDimitry Andric       HeaderContents += FilePath;
49981ad6265SDimitry Andric       HeaderContents += "\"\n";
50081ad6265SDimitry Andric       KnownInputFiles.emplace_back(FilePath, true);
50181ad6265SDimitry Andric     }
50281ad6265SDimitry Andric   }
50381ad6265SDimitry Andric 
50481ad6265SDimitry Andric   if (CI.getHeaderSearchOpts().Verbose)
50581ad6265SDimitry Andric     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
50681ad6265SDimitry Andric                                 << HeaderContents << "\n";
50781ad6265SDimitry Andric 
50881ad6265SDimitry Andric   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
50981ad6265SDimitry Andric                                                 getInputBufferName());
51081ad6265SDimitry Andric 
51181ad6265SDimitry Andric   // Set that buffer up as our "real" input in the CompilerInstance.
51281ad6265SDimitry Andric   Inputs.clear();
51381ad6265SDimitry Andric   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
51481ad6265SDimitry Andric 
51581ad6265SDimitry Andric   return true;
51681ad6265SDimitry Andric }
51781ad6265SDimitry Andric 
EndSourceFileAction()518*0fca6ea1SDimitry Andric void ExtractAPIAction::EndSourceFileAction() {
519*0fca6ea1SDimitry Andric   ImplEndSourceFileAction(getCompilerInstance());
520*0fca6ea1SDimitry Andric }
52181ad6265SDimitry Andric 
52206c3fb27SDimitry Andric std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)52306c3fb27SDimitry Andric WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI,
52406c3fb27SDimitry Andric                                             StringRef InFile) {
52506c3fb27SDimitry Andric   auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
52606c3fb27SDimitry Andric   if (!OtherConsumer)
52706c3fb27SDimitry Andric     return nullptr;
52806c3fb27SDimitry Andric 
52906c3fb27SDimitry Andric   CreatedASTConsumer = true;
53006c3fb27SDimitry Andric 
531*0fca6ea1SDimitry Andric   ProductName = CI.getFrontendOpts().ProductName;
532*0fca6ea1SDimitry Andric   auto InputFilename = llvm::sys::path::filename(InFile);
533*0fca6ea1SDimitry Andric   OS = createAdditionalSymbolGraphFile(CI, InputFilename);
53406c3fb27SDimitry Andric 
53506c3fb27SDimitry Andric   // Now that we have enough information about the language options and the
53606c3fb27SDimitry Andric   // target triple, let's create the APISet before anyone uses it.
53706c3fb27SDimitry Andric   API = std::make_unique<APISet>(
53806c3fb27SDimitry Andric       CI.getTarget().getTriple(),
53906c3fb27SDimitry Andric       CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
54006c3fb27SDimitry Andric 
54106c3fb27SDimitry Andric   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
54206c3fb27SDimitry Andric       CI.getSourceManager(), *API, CI.getPreprocessor()));
54306c3fb27SDimitry Andric 
54406c3fb27SDimitry Andric   // Do not include location in anonymous decls.
54506c3fb27SDimitry Andric   PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
54606c3fb27SDimitry Andric   Policy.AnonymousTagLocations = false;
54706c3fb27SDimitry Andric   CI.getASTContext().setPrintingPolicy(Policy);
54806c3fb27SDimitry Andric 
54906c3fb27SDimitry Andric   if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) {
55006c3fb27SDimitry Andric     llvm::handleAllErrors(
55106c3fb27SDimitry Andric         APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList,
55206c3fb27SDimitry Andric                                CI.getFileManager())
55306c3fb27SDimitry Andric             .moveInto(IgnoresList),
55406c3fb27SDimitry Andric         [&CI](const IgnoresFileNotFound &Err) {
55506c3fb27SDimitry Andric           CI.getDiagnostics().Report(
55606c3fb27SDimitry Andric               diag::err_extract_api_ignores_file_not_found)
55706c3fb27SDimitry Andric               << Err.Path;
55806c3fb27SDimitry Andric         });
55906c3fb27SDimitry Andric   }
56006c3fb27SDimitry Andric 
56106c3fb27SDimitry Andric   auto WrappingConsumer =
56206c3fb27SDimitry Andric       std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API);
56306c3fb27SDimitry Andric   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
56406c3fb27SDimitry Andric   Consumers.push_back(std::move(OtherConsumer));
56506c3fb27SDimitry Andric   Consumers.push_back(std::move(WrappingConsumer));
56606c3fb27SDimitry Andric 
56706c3fb27SDimitry Andric   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
56806c3fb27SDimitry Andric }
56906c3fb27SDimitry Andric 
EndSourceFileAction()57006c3fb27SDimitry Andric void WrappingExtractAPIAction::EndSourceFileAction() {
57106c3fb27SDimitry Andric   // Invoke wrapped action's method.
57206c3fb27SDimitry Andric   WrapperFrontendAction::EndSourceFileAction();
57306c3fb27SDimitry Andric 
57406c3fb27SDimitry Andric   if (CreatedASTConsumer) {
575*0fca6ea1SDimitry Andric     ImplEndSourceFileAction(getCompilerInstance());
57606c3fb27SDimitry Andric   }
57781ad6265SDimitry Andric }
578