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