aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/ExtractAPI
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/ExtractAPI')
-rw-r--r--clang/lib/ExtractAPI/API.cpp225
-rw-r--r--clang/lib/ExtractAPI/APIIgnoresList.cpp53
-rw-r--r--clang/lib/ExtractAPI/AvailabilityInfo.cpp50
-rw-r--r--clang/lib/ExtractAPI/DeclarationFragments.cpp34
-rw-r--r--clang/lib/ExtractAPI/ExtractAPIConsumer.cpp612
-rw-r--r--clang/lib/ExtractAPI/ExtractAPIVisitor.cpp560
-rw-r--r--clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp379
-rw-r--r--clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp7
-rw-r--r--clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h2
9 files changed, 1161 insertions, 761 deletions
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 073e36f320d3..553b7bbe710f 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -27,7 +27,8 @@ using namespace llvm;
namespace {
template <typename RecordTy, typename... CtorArgsTy>
-RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
+RecordTy *addTopLevelRecord(DenseMap<StringRef, APIRecord *> &USRLookupTable,
+ APISet::RecordMap<RecordTy> &RecordMap,
StringRef USR, CtorArgsTy &&...CtorArgs) {
auto Result = RecordMap.insert({USR, nullptr});
@@ -36,75 +37,99 @@ RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
Result.first->second =
std::make_unique<RecordTy>(USR, std::forward<CtorArgsTy>(CtorArgs)...);
- return Result.first->second.get();
+ auto *Record = Result.first->second.get();
+ USRLookupTable.insert({USR, Record});
+ return Record;
}
} // namespace
GlobalVariableRecord *
APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability, LinkageInfo Linkage,
+ AvailabilitySet Availabilities, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Fragments,
- DeclarationFragments SubHeading) {
- return addTopLevelRecord(GlobalVariables, USR, Name, Loc, Availability,
- Linkage, Comment, Fragments, SubHeading);
+ DeclarationFragments SubHeading, bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, GlobalVariables, USR, Name, Loc,
+ std::move(Availabilities), Linkage, Comment,
+ Fragments, SubHeading, IsFromSystemHeader);
}
GlobalFunctionRecord *APISet::addGlobalFunction(
StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability, LinkageInfo Linkage,
+ AvailabilitySet Availabilities, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Fragments,
- DeclarationFragments SubHeading, FunctionSignature Signature) {
- return addTopLevelRecord(GlobalFunctions, USR, Name, Loc, Availability,
- Linkage, Comment, Fragments, SubHeading, Signature);
+ DeclarationFragments SubHeading, FunctionSignature Signature,
+ bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, GlobalFunctions, USR, Name, Loc,
+ std::move(Availabilities), Linkage, Comment,
+ Fragments, SubHeading, Signature,
+ IsFromSystemHeader);
}
-EnumConstantRecord *APISet::addEnumConstant(
- EnumRecord *Enum, StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability, const DocComment &Comment,
- DeclarationFragments Declaration, DeclarationFragments SubHeading) {
+EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
+ StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availabilities,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
auto Record = std::make_unique<EnumConstantRecord>(
- USR, Name, Loc, Availability, Comment, Declaration, SubHeading);
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, IsFromSystemHeader);
+ Record->ParentInformation = APIRecord::HierarchyInformation(
+ Enum->USR, Enum->Name, Enum->getKind(), Enum);
+ USRBasedLookupTable.insert({USR, Record.get()});
return Enum->Constants.emplace_back(std::move(Record)).get();
}
EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability,
+ AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading) {
- return addTopLevelRecord(Enums, USR, Name, Loc, Availability, Comment,
- Declaration, SubHeading);
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, Enums, USR, Name, Loc,
+ std::move(Availabilities), Comment, Declaration,
+ SubHeading, IsFromSystemHeader);
}
StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability,
+ AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading) {
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
auto Record = std::make_unique<StructFieldRecord>(
- USR, Name, Loc, Availability, Comment, Declaration, SubHeading);
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, IsFromSystemHeader);
+ Record->ParentInformation = APIRecord::HierarchyInformation(
+ Struct->USR, Struct->Name, Struct->getKind(), Struct);
+ USRBasedLookupTable.insert({USR, Record.get()});
return Struct->Fields.emplace_back(std::move(Record)).get();
}
StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability,
+ AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading) {
- return addTopLevelRecord(Structs, USR, Name, Loc, Availability, Comment,
- Declaration, SubHeading);
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, Structs, USR, Name, Loc,
+ std::move(Availabilities), Comment, Declaration,
+ SubHeading, IsFromSystemHeader);
}
ObjCCategoryRecord *APISet::addObjCCategory(
StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability, const DocComment &Comment,
+ AvailabilitySet Availabilities, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading,
- SymbolReference Interface) {
+ SymbolReference Interface, bool IsFromSystemHeader) {
// Create the category record.
- auto *Record = addTopLevelRecord(ObjCCategories, USR, Name, Loc, Availability,
- Comment, Declaration, SubHeading, Interface);
+ auto *Record =
+ addTopLevelRecord(USRBasedLookupTable, ObjCCategories, USR, Name, Loc,
+ std::move(Availabilities), Comment, Declaration,
+ SubHeading, Interface, IsFromSystemHeader);
// If this category is extending a known interface, associate it with the
// ObjCInterfaceRecord.
@@ -115,76 +140,119 @@ ObjCCategoryRecord *APISet::addObjCCategory(
return Record;
}
-ObjCInterfaceRecord *APISet::addObjCInterface(
- StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability, LinkageInfo Linkage,
- const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading, SymbolReference SuperClass) {
- return addTopLevelRecord(ObjCInterfaces, USR, Name, Loc, Availability,
- Linkage, Comment, Declaration, SubHeading,
- SuperClass);
+ObjCInterfaceRecord *
+APISet::addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availabilities, LinkageInfo Linkage,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading,
+ SymbolReference SuperClass, bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, ObjCInterfaces, USR, Name, Loc,
+ std::move(Availabilities), Linkage, Comment,
+ Declaration, SubHeading, SuperClass,
+ IsFromSystemHeader);
}
ObjCMethodRecord *APISet::addObjCMethod(
ObjCContainerRecord *Container, StringRef Name, StringRef USR,
- PresumedLoc Loc, const AvailabilityInfo &Availability,
- const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading, FunctionSignature Signature,
- bool IsInstanceMethod) {
- auto Record = std::make_unique<ObjCMethodRecord>(
- USR, Name, Loc, Availability, Comment, Declaration, SubHeading, Signature,
- IsInstanceMethod);
+ PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ FunctionSignature Signature, bool IsInstanceMethod,
+ bool IsFromSystemHeader) {
+ std::unique_ptr<ObjCMethodRecord> Record;
+ if (IsInstanceMethod)
+ Record = std::make_unique<ObjCInstanceMethodRecord>(
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, Signature, IsFromSystemHeader);
+ else
+ Record = std::make_unique<ObjCClassMethodRecord>(
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, Signature, IsFromSystemHeader);
+
+ Record->ParentInformation = APIRecord::HierarchyInformation(
+ Container->USR, Container->Name, Container->getKind(), Container);
+ USRBasedLookupTable.insert({USR, Record.get()});
return Container->Methods.emplace_back(std::move(Record)).get();
}
ObjCPropertyRecord *APISet::addObjCProperty(
ObjCContainerRecord *Container, StringRef Name, StringRef USR,
- PresumedLoc Loc, const AvailabilityInfo &Availability,
- const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading,
+ PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName,
- StringRef SetterName, bool IsOptional) {
- auto Record = std::make_unique<ObjCPropertyRecord>(
- USR, Name, Loc, Availability, Comment, Declaration, SubHeading,
- Attributes, GetterName, SetterName, IsOptional);
+ StringRef SetterName, bool IsOptional, bool IsInstanceProperty,
+ bool IsFromSystemHeader) {
+ std::unique_ptr<ObjCPropertyRecord> Record;
+ if (IsInstanceProperty)
+ Record = std::make_unique<ObjCInstancePropertyRecord>(
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, Attributes, GetterName, SetterName, IsOptional,
+ IsFromSystemHeader);
+ else
+ Record = std::make_unique<ObjCClassPropertyRecord>(
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, Attributes, GetterName, SetterName, IsOptional,
+ IsFromSystemHeader);
+ Record->ParentInformation = APIRecord::HierarchyInformation(
+ Container->USR, Container->Name, Container->getKind(), Container);
+ USRBasedLookupTable.insert({USR, Record.get()});
return Container->Properties.emplace_back(std::move(Record)).get();
}
ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable(
ObjCContainerRecord *Container, StringRef Name, StringRef USR,
- PresumedLoc Loc, const AvailabilityInfo &Availability,
- const DocComment &Comment, DeclarationFragments Declaration,
- DeclarationFragments SubHeading,
- ObjCInstanceVariableRecord::AccessControl Access) {
+ PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ ObjCInstanceVariableRecord::AccessControl Access, bool IsFromSystemHeader) {
auto Record = std::make_unique<ObjCInstanceVariableRecord>(
- USR, Name, Loc, Availability, Comment, Declaration, SubHeading, Access);
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, Access, IsFromSystemHeader);
+ Record->ParentInformation = APIRecord::HierarchyInformation(
+ Container->USR, Container->Name, Container->getKind(), Container);
+ USRBasedLookupTable.insert({USR, Record.get()});
return Container->Ivars.emplace_back(std::move(Record)).get();
}
-ObjCProtocolRecord *APISet::addObjCProtocol(
- StringRef Name, StringRef USR, PresumedLoc Loc,
- const AvailabilityInfo &Availability, const DocComment &Comment,
- DeclarationFragments Declaration, DeclarationFragments SubHeading) {
- return addTopLevelRecord(ObjCProtocols, USR, Name, Loc, Availability, Comment,
- Declaration, SubHeading);
+ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR,
+ PresumedLoc Loc,
+ AvailabilitySet Availabilities,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, ObjCProtocols, USR, Name, Loc,
+ std::move(Availabilities), Comment, Declaration,
+ SubHeading, IsFromSystemHeader);
}
MacroDefinitionRecord *
APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading) {
- return addTopLevelRecord(Macros, USR, Name, Loc, Declaration, SubHeading);
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, Macros, USR, Name, Loc,
+ Declaration, SubHeading, IsFromSystemHeader);
+}
+
+TypedefRecord *
+APISet::addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading,
+ SymbolReference UnderlyingType, bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, Typedefs, USR, Name, Loc,
+ std::move(Availabilities), Comment, Declaration,
+ SubHeading, UnderlyingType, IsFromSystemHeader);
}
-TypedefRecord *APISet::addTypedef(StringRef Name, StringRef USR,
- PresumedLoc Loc,
- const AvailabilityInfo &Availability,
- const DocComment &Comment,
- DeclarationFragments Declaration,
- DeclarationFragments SubHeading,
- SymbolReference UnderlyingType) {
- return addTopLevelRecord(Typedefs, USR, Name, Loc, Availability, Comment,
- Declaration, SubHeading, UnderlyingType);
+APIRecord *APISet::findRecordForUSR(StringRef USR) const {
+ if (USR.empty())
+ return nullptr;
+
+ auto It = USRBasedLookupTable.find(USR);
+ if (It != USRBasedLookupTable.end())
+ return It->second;
+ return nullptr;
}
StringRef APISet::recordUSR(const Decl *D) {
@@ -214,8 +282,9 @@ StringRef APISet::copyString(StringRef String) {
}
APIRecord::~APIRecord() {}
-
ObjCContainerRecord::~ObjCContainerRecord() {}
+ObjCMethodRecord::~ObjCMethodRecord() {}
+ObjCPropertyRecord::~ObjCPropertyRecord() {}
void GlobalFunctionRecord::anchor() {}
void GlobalVariableRecord::anchor() {}
@@ -223,9 +292,11 @@ void EnumConstantRecord::anchor() {}
void EnumRecord::anchor() {}
void StructFieldRecord::anchor() {}
void StructRecord::anchor() {}
-void ObjCPropertyRecord::anchor() {}
+void ObjCInstancePropertyRecord::anchor() {}
+void ObjCClassPropertyRecord::anchor() {}
void ObjCInstanceVariableRecord::anchor() {}
-void ObjCMethodRecord::anchor() {}
+void ObjCInstanceMethodRecord::anchor() {}
+void ObjCClassMethodRecord::anchor() {}
void ObjCCategoryRecord::anchor() {}
void ObjCInterfaceRecord::anchor() {}
void ObjCProtocolRecord::anchor() {}
diff --git a/clang/lib/ExtractAPI/APIIgnoresList.cpp b/clang/lib/ExtractAPI/APIIgnoresList.cpp
new file mode 100644
index 000000000000..1d65ae2b8e31
--- /dev/null
+++ b/clang/lib/ExtractAPI/APIIgnoresList.cpp
@@ -0,0 +1,53 @@
+//===- ExtractAPI/APIIgnoresList.cpp -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements APIIgnoresList that allows users to specifiy a file
+/// containing symbols to ignore during API extraction.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ExtractAPI/APIIgnoresList.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+
+using namespace clang;
+using namespace clang::extractapi;
+using namespace llvm;
+
+char IgnoresFileNotFound::ID;
+
+void IgnoresFileNotFound::log(llvm::raw_ostream &os) const {
+ os << "Could not find API ignores file " << Path;
+}
+
+std::error_code IgnoresFileNotFound::convertToErrorCode() const {
+ return llvm::inconvertibleErrorCode();
+}
+
+Expected<APIIgnoresList> APIIgnoresList::create(StringRef IgnoresFilePath,
+ FileManager &FM) {
+ auto BufferOrErr = FM.getBufferForFile(IgnoresFilePath);
+ if (!BufferOrErr)
+ return make_error<IgnoresFileNotFound>(IgnoresFilePath);
+
+ auto Buffer = std::move(BufferOrErr.get());
+ SmallVector<StringRef, 32> Lines;
+ Buffer->getBuffer().split(Lines, '\n', /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ // Symbol names don't have spaces in them, let's just remove these in case the
+ // input is slighlty malformed.
+ transform(Lines, Lines.begin(), [](StringRef Line) { return Line.trim(); });
+ sort(Lines);
+ return APIIgnoresList(std::move(Lines), std::move(Buffer));
+}
+
+bool APIIgnoresList::shouldIgnore(StringRef SymbolName) const {
+ auto It = lower_bound(SymbolsToIgnore, SymbolName);
+ return (It != SymbolsToIgnore.end()) && (*It == SymbolName);
+}
diff --git a/clang/lib/ExtractAPI/AvailabilityInfo.cpp b/clang/lib/ExtractAPI/AvailabilityInfo.cpp
new file mode 100644
index 000000000000..ada64cfb92e6
--- /dev/null
+++ b/clang/lib/ExtractAPI/AvailabilityInfo.cpp
@@ -0,0 +1,50 @@
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/AST/Attr.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace clang;
+using namespace extractapi;
+
+AvailabilitySet::AvailabilitySet(const Decl *Decl) {
+ // Collect availability attributes from all redeclrations.
+ for (const auto *RD : Decl->redecls()) {
+ if (const auto *A = RD->getAttr<UnavailableAttr>()) {
+ if (!A->isImplicit()) {
+ this->Availabilities.clear();
+ UnconditionallyUnavailable = true;
+ }
+ }
+
+ if (const auto *A = RD->getAttr<DeprecatedAttr>()) {
+ if (!A->isImplicit()) {
+ this->Availabilities.clear();
+ UnconditionallyDeprecated = true;
+ }
+ }
+
+ for (const auto *Attr : RD->specific_attrs<AvailabilityAttr>()) {
+ StringRef Domain = Attr->getPlatform()->getName();
+ auto *Availability =
+ llvm::find_if(Availabilities, [Domain](const AvailabilityInfo &Info) {
+ return Domain.equals(Info.Domain);
+ });
+ if (Availability != Availabilities.end()) {
+ // Get the highest introduced version for all redeclarations.
+ if (Availability->Introduced < Attr->getIntroduced())
+ Availability->Introduced = Attr->getIntroduced();
+
+ // Get the lowest deprecated version for all redeclarations.
+ if (Availability->Deprecated > Attr->getDeprecated())
+ Availability->Deprecated = Attr->getDeprecated();
+
+ // Get the lowest obsoleted version for all redeclarations.
+ if (Availability->Obsoleted > Attr->getObsoleted())
+ Availability->Obsoleted = Attr->getObsoleted();
+ } else {
+ Availabilities.emplace_back(Domain, Attr->getIntroduced(),
+ Attr->getDeprecated(),
+ Attr->getObsoleted());
+ }
+ }
+ }
+}
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 75d360a3ba16..12c91c582aa9 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -109,7 +109,7 @@ DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS,
SmallString<128> USR;
index::generateUSRForDecl(NS, USR);
Fragments.append(NS->getName(),
- DeclarationFragments::FragmentKind::Identifier, USR);
+ DeclarationFragments::FragmentKind::Identifier, USR, NS);
break;
}
@@ -118,7 +118,8 @@ DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS,
SmallString<128> USR;
index::generateUSRForDecl(Alias, USR);
Fragments.append(Alias->getName(),
- DeclarationFragments::FragmentKind::Identifier, USR);
+ DeclarationFragments::FragmentKind::Identifier, USR,
+ Alias);
break;
}
@@ -136,7 +137,7 @@ DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS,
Fragments.append("template", DeclarationFragments::FragmentKind::Keyword);
Fragments.appendSpace();
// Fallthrough after adding the keyword to handle the actual type.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case NestedNameSpecifier::TypeSpec: {
const Type *T = NNS->getAsType();
@@ -255,11 +256,11 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
// direct reference to the typedef instead of the wrapped type.
if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(T)) {
const TypedefNameDecl *Decl = TypedefTy->getDecl();
- std::string USR =
- TypedefUnderlyingTypeResolver(Context).getUSRForType(QualType(T, 0));
- return Fragments.append(Decl->getName(),
- DeclarationFragments::FragmentKind::TypeIdentifier,
- USR);
+ TypedefUnderlyingTypeResolver TypedefResolver(Context);
+ std::string USR = TypedefResolver.getUSRForType(QualType(T, 0));
+ return Fragments.append(
+ Decl->getName(), DeclarationFragments::FragmentKind::TypeIdentifier,
+ USR, TypedefResolver.getUnderlyingTypeDecl(QualType(T, 0)));
}
// If the base type is a TagType (struct/interface/union/class/enum), let's
@@ -273,7 +274,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
clang::index::generateUSRForDecl(Decl, TagUSR);
return Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier,
- TagUSR);
+ TagUSR, Decl);
}
// If the base type is an ObjCInterfaceType, use the underlying
@@ -284,7 +285,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
index::generateUSRForDecl(Decl, USR);
return Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::TypeIdentifier,
- USR);
+ USR, Decl);
}
// Default fragment builder for other kinds of types (BuiltinType etc.)
@@ -530,13 +531,15 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory(
const ObjCCategoryDecl *Category) {
DeclarationFragments Fragments;
+ auto *Interface = Category->getClassInterface();
SmallString<128> InterfaceUSR;
- index::generateUSRForDecl(Category->getClassInterface(), InterfaceUSR);
+ index::generateUSRForDecl(Interface, InterfaceUSR);
Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
.appendSpace()
.append(Category->getClassInterface()->getName(),
- DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR)
+ DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR,
+ Interface)
.append(" (", DeclarationFragments::FragmentKind::Text)
.append(Category->getName(),
DeclarationFragments::FragmentKind::Identifier)
@@ -560,7 +563,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCInterface(
index::generateUSRForDecl(SuperClass, SuperUSR);
Fragments.append(" : ", DeclarationFragments::FragmentKind::Text)
.append(SuperClass->getName(),
- DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR);
+ DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR,
+ SuperClass);
}
return Fragments;
@@ -692,6 +696,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
return Fragments.appendSpace()
.append(getFragmentsForType(Property->getType(),
Property->getASTContext(), After))
+ .appendSpace()
.append(Property->getName(),
DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
@@ -718,7 +723,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
SmallString<128> USR;
index::generateUSRForDecl(*It, USR);
Fragments.append((*It)->getName(),
- DeclarationFragments::FragmentKind::TypeIdentifier, USR);
+ DeclarationFragments::FragmentKind::TypeIdentifier, USR,
+ *It);
}
Fragments.append(">", DeclarationFragments::FragmentKind::Text);
}
diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
index 1a785182e363..644845efb819 100644
--- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
+++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -7,25 +7,20 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to
-/// collect API information.
+/// This file implements the ExtractAPIAction, and ASTConsumer to collect API
+/// information.
///
//===----------------------------------------------------------------------===//
-#include "TypedefUnderlyingTypeResolver.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/ParentMapContext.h"
-#include "clang/AST/RawCommentList.h"
-#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/ExtractAPI/API.h"
-#include "clang/ExtractAPI/AvailabilityInfo.h"
-#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/ExtractAPI/APIIgnoresList.h"
+#include "clang/ExtractAPI/ExtractAPIVisitor.h"
#include "clang/ExtractAPI/FrontendActions.h"
#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
#include "clang/Frontend/ASTConsumers.h"
@@ -38,12 +33,14 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
+#include <optional>
#include <utility>
using namespace clang;
@@ -51,16 +48,9 @@ using namespace extractapi;
namespace {
-StringRef getTypedefName(const TagDecl *Decl) {
- if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
- return TypedefDecl->getName();
-
- return {};
-}
-
-Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
- StringRef File,
- bool *IsQuoted = nullptr) {
+std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
+ StringRef File,
+ bool *IsQuoted = nullptr) {
assert(CI.hasFileManager() &&
"CompilerInstance does not have a FileNamager!");
@@ -154,7 +144,7 @@ Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
Rule.match(File, &Matches);
// Returned matches are always in stable order.
if (Matches.size() != 4)
- return None;
+ return std::nullopt;
return path::convert_to_slash(
(Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
@@ -169,11 +159,11 @@ Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
}
// Couldn't determine a include name, use full path instead.
- return None;
+ return std::nullopt;
}
struct LocationFileChecker {
- bool isLocationInKnownFile(SourceLocation Loc) {
+ bool operator()(SourceLocation Loc) {
// If the loc refers to a macro expansion we need to first get the file
// location of the expansion.
auto &SM = CI.getSourceManager();
@@ -229,554 +219,6 @@ private:
llvm::DenseSet<const FileEntry *> ExternalFileEntries;
};
-/// The RecursiveASTVisitor to traverse symbol declarations and collect API
-/// information.
-class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
-public:
- ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
- : Context(Context), API(API), LCF(LCF) {}
-
- const APISet &getAPI() const { return API; }
-
- bool VisitVarDecl(const VarDecl *Decl) {
- // Skip function parameters.
- if (isa<ParmVarDecl>(Decl))
- return true;
-
- // Skip non-global variables in records (struct/union/class).
- if (Decl->getDeclContext()->isRecord())
- return true;
-
- // Skip local variables inside function or method.
- if (!Decl->isDefinedOutsideFunctionOrMethod())
- return true;
-
- // If this is a template but not specialization or instantiation, skip.
- if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
- Decl->getTemplateSpecializationKind() == TSK_Undeclared)
- return true;
-
- if (!LCF.isLocationInKnownFile(Decl->getLocation()))
- return true;
-
- // Collect symbol information.
- StringRef Name = Decl->getName();
- StringRef USR = API.recordUSR(Decl);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- AvailabilityInfo Availability = getAvailability(Decl);
- LinkageInfo Linkage = Decl->getLinkageAndVisibility();
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the variable.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Decl);
-
- // Add the global variable record to the API set.
- API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
- Declaration, SubHeading);
- return true;
- }
-
- bool VisitFunctionDecl(const FunctionDecl *Decl) {
- if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
- // Skip member function in class templates.
- if (Method->getParent()->getDescribedClassTemplate() != nullptr)
- return true;
-
- // Skip methods in records.
- for (auto P : Context.getParents(*Method)) {
- if (P.get<CXXRecordDecl>())
- return true;
- }
-
- // Skip ConstructorDecl and DestructorDecl.
- if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
- return true;
- }
-
- // Skip templated functions.
- switch (Decl->getTemplatedKind()) {
- case FunctionDecl::TK_NonTemplate:
- case FunctionDecl::TK_DependentNonTemplate:
- break;
- case FunctionDecl::TK_MemberSpecialization:
- case FunctionDecl::TK_FunctionTemplateSpecialization:
- if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
- if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
- return true;
- }
- break;
- case FunctionDecl::TK_FunctionTemplate:
- case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
- return true;
- }
-
- if (!LCF.isLocationInKnownFile(Decl->getLocation()))
- return true;
-
- // Collect symbol information.
- StringRef Name = Decl->getName();
- StringRef USR = API.recordUSR(Decl);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- AvailabilityInfo Availability = getAvailability(Decl);
- LinkageInfo Linkage = Decl->getLinkageAndVisibility();
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments, sub-heading, and signature of the function.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Decl);
- FunctionSignature Signature =
- DeclarationFragmentsBuilder::getFunctionSignature(Decl);
-
- // Add the function record to the API set.
- API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment,
- Declaration, SubHeading, Signature);
- return true;
- }
-
- bool VisitEnumDecl(const EnumDecl *Decl) {
- if (!Decl->isComplete())
- return true;
-
- // Skip forward declaration.
- if (!Decl->isThisDeclarationADefinition())
- return true;
-
- if (!LCF.isLocationInKnownFile(Decl->getLocation()))
- return true;
-
- // Collect symbol information.
- std::string NameString = Decl->getQualifiedNameAsString();
- StringRef Name(NameString);
- if (Name.empty())
- Name = getTypedefName(Decl);
-
- StringRef USR = API.recordUSR(Decl);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- AvailabilityInfo Availability = getAvailability(Decl);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the enum.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Decl);
-
- EnumRecord *EnumRecord =
- API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment,
- Declaration, SubHeading);
-
- // Now collect information about the enumerators in this enum.
- recordEnumConstants(EnumRecord, Decl->enumerators());
-
- return true;
- }
-
- bool VisitRecordDecl(const RecordDecl *Decl) {
- if (!Decl->isCompleteDefinition())
- return true;
-
- // Skip C++ structs/classes/unions
- // TODO: support C++ records
- if (isa<CXXRecordDecl>(Decl))
- return true;
-
- if (!LCF.isLocationInKnownFile(Decl->getLocation()))
- return true;
-
- // Collect symbol information.
- StringRef Name = Decl->getName();
- if (Name.empty())
- Name = getTypedefName(Decl);
- StringRef USR = API.recordUSR(Decl);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- AvailabilityInfo Availability = getAvailability(Decl);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the struct.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Decl);
-
- StructRecord *StructRecord = API.addStruct(
- Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
-
- // Now collect information about the fields in this struct.
- recordStructFields(StructRecord, Decl->fields());
-
- return true;
- }
-
- bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
- // Skip forward declaration for classes (@class)
- if (!Decl->isThisDeclarationADefinition())
- return true;
-
- if (!LCF.isLocationInKnownFile(Decl->getLocation()))
- return true;
-
- // Collect symbol information.
- StringRef Name = Decl->getName();
- StringRef USR = API.recordUSR(Decl);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- AvailabilityInfo Availability = getAvailability(Decl);
- LinkageInfo Linkage = Decl->getLinkageAndVisibility();
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the interface.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Decl);
-
- // Collect super class information.
- SymbolReference SuperClass;
- if (const auto *SuperClassDecl = Decl->getSuperClass()) {
- SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
- SuperClass.USR = API.recordUSR(SuperClassDecl);
- }
-
- ObjCInterfaceRecord *ObjCInterfaceRecord =
- API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
- Declaration, SubHeading, SuperClass);
-
- // Record all methods (selectors). This doesn't include automatically
- // synthesized property methods.
- recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
- recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
- recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
- recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
-
- return true;
- }
-
- bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
- // Skip forward declaration for protocols (@protocol).
- if (!Decl->isThisDeclarationADefinition())
- return true;
-
- if (!LCF.isLocationInKnownFile(Decl->getLocation()))
- return true;
-
- // Collect symbol information.
- StringRef Name = Decl->getName();
- StringRef USR = API.recordUSR(Decl);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- AvailabilityInfo Availability = getAvailability(Decl);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the protocol.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Decl);
-
- ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
- Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
-
- recordObjCMethods(ObjCProtocolRecord, Decl->methods());
- recordObjCProperties(ObjCProtocolRecord, Decl->properties());
- recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
-
- return true;
- }
-
- bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
- // Skip ObjC Type Parameter for now.
- if (isa<ObjCTypeParamDecl>(Decl))
- return true;
-
- if (!Decl->isDefinedOutsideFunctionOrMethod())
- return true;
-
- if (!LCF.isLocationInKnownFile(Decl->getLocation()))
- return true;
-
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- StringRef Name = Decl->getName();
- AvailabilityInfo Availability = getAvailability(Decl);
- StringRef USR = API.recordUSR(Decl);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- QualType Type = Decl->getUnderlyingType();
- SymbolReference SymRef =
- TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
- API);
-
- API.addTypedef(Name, USR, Loc, Availability, Comment,
- DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
- DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
-
- return true;
- }
-
- bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
- // Collect symbol information.
- StringRef Name = Decl->getName();
- StringRef USR = API.recordUSR(Decl);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Decl->getLocation());
- AvailabilityInfo Availability = getAvailability(Decl);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
- // Build declaration fragments and sub-heading for the category.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Decl);
-
- const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
- SymbolReference Interface(InterfaceDecl->getName(),
- API.recordUSR(InterfaceDecl));
-
- ObjCCategoryRecord *ObjCCategoryRecord =
- API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
- SubHeading, Interface);
-
- recordObjCMethods(ObjCCategoryRecord, Decl->methods());
- recordObjCProperties(ObjCCategoryRecord, Decl->properties());
- recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
- recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
-
- return true;
- }
-
-private:
- /// Get availability information of the declaration \p D.
- AvailabilityInfo getAvailability(const Decl *D) const {
- StringRef PlatformName = Context.getTargetInfo().getPlatformName();
-
- AvailabilityInfo Availability;
- // Collect availability attributes from all redeclarations.
- for (const auto *RD : D->redecls()) {
- for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
- if (A->getPlatform()->getName() != PlatformName)
- continue;
- Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
- A->getObsoleted(), A->getUnavailable(),
- /* UnconditionallyDeprecated */ false,
- /* UnconditionallyUnavailable */ false);
- break;
- }
-
- if (const auto *A = RD->getAttr<UnavailableAttr>())
- if (!A->isImplicit()) {
- Availability.Unavailable = true;
- Availability.UnconditionallyUnavailable = true;
- }
-
- if (const auto *A = RD->getAttr<DeprecatedAttr>())
- if (!A->isImplicit())
- Availability.UnconditionallyDeprecated = true;
- }
-
- return Availability;
- }
-
- /// Collect API information for the enum constants and associate with the
- /// parent enum.
- void recordEnumConstants(EnumRecord *EnumRecord,
- const EnumDecl::enumerator_range Constants) {
- for (const auto *Constant : Constants) {
- // Collect symbol information.
- StringRef Name = Constant->getName();
- StringRef USR = API.recordUSR(Constant);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Constant->getLocation());
- AvailabilityInfo Availability = getAvailability(Constant);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the enum constant.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Constant);
-
- API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
- Declaration, SubHeading);
- }
- }
-
- /// Collect API information for the struct fields and associate with the
- /// parent struct.
- void recordStructFields(StructRecord *StructRecord,
- const RecordDecl::field_range Fields) {
- for (const auto *Field : Fields) {
- // Collect symbol information.
- StringRef Name = Field->getName();
- StringRef USR = API.recordUSR(Field);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Field->getLocation());
- AvailabilityInfo Availability = getAvailability(Field);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the struct field.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForField(Field);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Field);
-
- API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
- Declaration, SubHeading);
- }
- }
-
- /// Collect API information for the Objective-C methods and associate with the
- /// parent container.
- void recordObjCMethods(ObjCContainerRecord *Container,
- const ObjCContainerDecl::method_range Methods) {
- for (const auto *Method : Methods) {
- // Don't record selectors for properties.
- if (Method->isPropertyAccessor())
- continue;
-
- StringRef Name = API.copyString(Method->getSelector().getAsString());
- StringRef USR = API.recordUSR(Method);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Method->getLocation());
- AvailabilityInfo Availability = getAvailability(Method);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments, sub-heading, and signature for the method.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Method);
- FunctionSignature Signature =
- DeclarationFragmentsBuilder::getFunctionSignature(Method);
-
- API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
- Declaration, SubHeading, Signature,
- Method->isInstanceMethod());
- }
- }
-
- void recordObjCProperties(ObjCContainerRecord *Container,
- const ObjCContainerDecl::prop_range Properties) {
- for (const auto *Property : Properties) {
- StringRef Name = Property->getName();
- StringRef USR = API.recordUSR(Property);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Property->getLocation());
- AvailabilityInfo Availability = getAvailability(Property);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the property.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Property);
-
- StringRef GetterName =
- API.copyString(Property->getGetterName().getAsString());
- StringRef SetterName =
- API.copyString(Property->getSetterName().getAsString());
-
- // Get the attributes for property.
- unsigned Attributes = ObjCPropertyRecord::NoAttr;
- if (Property->getPropertyAttributes() &
- ObjCPropertyAttribute::kind_readonly)
- Attributes |= ObjCPropertyRecord::ReadOnly;
- if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
- Attributes |= ObjCPropertyRecord::Class;
-
- API.addObjCProperty(
- Container, Name, USR, Loc, Availability, Comment, Declaration,
- SubHeading,
- static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
- GetterName, SetterName, Property->isOptional());
- }
- }
-
- void recordObjCInstanceVariables(
- ObjCContainerRecord *Container,
- const llvm::iterator_range<
- DeclContext::specific_decl_iterator<ObjCIvarDecl>>
- Ivars) {
- for (const auto *Ivar : Ivars) {
- StringRef Name = Ivar->getName();
- StringRef USR = API.recordUSR(Ivar);
- PresumedLoc Loc =
- Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
- AvailabilityInfo Availability = getAvailability(Ivar);
- DocComment Comment;
- if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
- Comment = RawComment->getFormattedLines(Context.getSourceManager(),
- Context.getDiagnostics());
-
- // Build declaration fragments and sub-heading for the instance variable.
- DeclarationFragments Declaration =
- DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
- DeclarationFragments SubHeading =
- DeclarationFragmentsBuilder::getSubHeading(Ivar);
-
- ObjCInstanceVariableRecord::AccessControl Access =
- Ivar->getCanonicalAccessControl();
-
- API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
- Comment, Declaration, SubHeading, Access);
- }
- }
-
- void recordObjCProtocols(ObjCContainerRecord *Container,
- ObjCInterfaceDecl::protocol_range Protocols) {
- for (const auto *Protocol : Protocols)
- Container->Protocols.emplace_back(Protocol->getName(),
- API.recordUSR(Protocol));
- }
-
- ASTContext &Context;
- APISet &API;
- LocationFileChecker &LCF;
-};
-
class ExtractAPIConsumer : public ASTConsumer {
public:
ExtractAPIConsumer(ASTContext &Context,
@@ -837,7 +279,7 @@ public:
if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
continue;
- if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
+ if (!LCF(PM.MacroNameToken.getLocation()))
continue;
StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
@@ -848,7 +290,8 @@ public:
API.addMacroDefinition(
Name, USR, Loc,
DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
- DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
+ DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
+ SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
}
PendingMacros.clear();
@@ -878,19 +321,36 @@ ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
if (!OS)
return nullptr;
- ProductName = CI.getFrontendOpts().ProductName;
+ auto ProductName = CI.getFrontendOpts().ProductName;
// Now that we have enough information about the language options and the
// target triple, let's create the APISet before anyone uses it.
API = std::make_unique<APISet>(
CI.getTarget().getTriple(),
- CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
+ CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
+ // Do not include location in anonymous decls.
+ PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy();
+ Policy.AnonymousTagLocations = false;
+ CI.getASTContext().setPrintingPolicy(Policy);
+
+ if (!CI.getFrontendOpts().ExtractAPIIgnoresFile.empty()) {
+ llvm::handleAllErrors(
+ APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFile,
+ CI.getFileManager())
+ .moveInto(IgnoresList),
+ [&CI](const IgnoresFileNotFound &Err) {
+ CI.getDiagnostics().Report(
+ diag::err_extract_api_ignores_file_not_found)
+ << Err.Path;
+ });
+ }
+
return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
std::move(LCF), *API);
}
@@ -959,7 +419,7 @@ void ExtractAPIAction::EndSourceFileAction() {
// Setup a SymbolGraphSerializer to write out collected API information in
// the Symbol Graph format.
// FIXME: Make the kind of APISerializer configurable.
- SymbolGraphSerializer SGSerializer(*API, ProductName);
+ SymbolGraphSerializer SGSerializer(*API, IgnoresList);
SGSerializer.serialize(*OS);
OS.reset();
}
diff --git a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
new file mode 100644
index 000000000000..24260cf89383
--- /dev/null
+++ b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp
@@ -0,0 +1,560 @@
+//===- ExtractAPI/ExtractAPIVisitor.cpp -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements the ExtractAPIVisitor an ASTVisitor to collect API
+/// information.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ExtractAPI/ExtractAPIVisitor.h"
+
+#include "TypedefUnderlyingTypeResolver.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/RawCommentList.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/AvailabilityInfo.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace extractapi;
+
+namespace {
+
+StringRef getTypedefName(const TagDecl *Decl) {
+ if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
+ return TypedefDecl->getName();
+
+ return {};
+}
+
+template <class DeclTy>
+bool isInSystemHeader(const ASTContext &Context, const DeclTy *D) {
+ return Context.getSourceManager().isInSystemHeader(D->getLocation());
+}
+
+} // namespace
+
+bool ExtractAPIVisitor::VisitVarDecl(const VarDecl *Decl) {
+ // skip function parameters.
+ if (isa<ParmVarDecl>(Decl))
+ return true;
+
+ // Skip non-global variables in records (struct/union/class).
+ if (Decl->getDeclContext()->isRecord())
+ return true;
+
+ // Skip local variables inside function or method.
+ if (!Decl->isDefinedOutsideFunctionOrMethod())
+ return true;
+
+ // If this is a template but not specialization or instantiation, skip.
+ if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
+ Decl->getTemplateSpecializationKind() == TSK_Undeclared)
+ return true;
+
+ if (!LocationChecker(Decl->getLocation()))
+ return true;
+
+ // Collect symbol information.
+ StringRef Name = Decl->getName();
+ StringRef USR = API.recordUSR(Decl);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the variable.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ // Add the global variable record to the API set.
+ API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+ Declaration, SubHeading, isInSystemHeader(Context, Decl));
+ return true;
+}
+
+bool ExtractAPIVisitor::VisitFunctionDecl(const FunctionDecl *Decl) {
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
+ // Skip member function in class templates.
+ if (Method->getParent()->getDescribedClassTemplate() != nullptr)
+ return true;
+
+ // Skip methods in records.
+ for (auto P : Context.getParents(*Method)) {
+ if (P.get<CXXRecordDecl>())
+ return true;
+ }
+
+ // Skip ConstructorDecl and DestructorDecl.
+ if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
+ return true;
+ }
+
+ // Skip templated functions.
+ switch (Decl->getTemplatedKind()) {
+ case FunctionDecl::TK_NonTemplate:
+ case FunctionDecl::TK_DependentNonTemplate:
+ break;
+ case FunctionDecl::TK_MemberSpecialization:
+ case FunctionDecl::TK_FunctionTemplateSpecialization:
+ if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
+ if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
+ return true;
+ }
+ break;
+ case FunctionDecl::TK_FunctionTemplate:
+ case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
+ return true;
+ }
+
+ if (!LocationChecker(Decl->getLocation()))
+ return true;
+
+ // Collect symbol information.
+ StringRef Name = Decl->getName();
+ StringRef USR = API.recordUSR(Decl);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments, sub-heading, and signature of the function.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+ FunctionSignature Signature =
+ DeclarationFragmentsBuilder::getFunctionSignature(Decl);
+
+ // Add the function record to the API set.
+ API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
+ Declaration, SubHeading, Signature,
+ isInSystemHeader(Context, Decl));
+ return true;
+}
+
+bool ExtractAPIVisitor::VisitEnumDecl(const EnumDecl *Decl) {
+ if (!Decl->isComplete())
+ return true;
+
+ // Skip forward declaration.
+ if (!Decl->isThisDeclarationADefinition())
+ return true;
+
+ if (!LocationChecker(Decl->getLocation()))
+ return true;
+
+ SmallString<128> QualifiedNameBuffer;
+ // Collect symbol information.
+ StringRef Name = Decl->getName();
+ if (Name.empty())
+ Name = getTypedefName(Decl);
+ if (Name.empty()) {
+ llvm::raw_svector_ostream OS(QualifiedNameBuffer);
+ Decl->printQualifiedName(OS);
+ Name = QualifiedNameBuffer.str();
+ }
+
+ StringRef USR = API.recordUSR(Decl);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the enum.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ EnumRecord *EnumRecord = API.addEnum(
+ API.copyString(Name), USR, Loc, AvailabilitySet(Decl), Comment,
+ Declaration, SubHeading, isInSystemHeader(Context, Decl));
+
+ // Now collect information about the enumerators in this enum.
+ recordEnumConstants(EnumRecord, Decl->enumerators());
+
+ return true;
+}
+
+bool ExtractAPIVisitor::VisitRecordDecl(const RecordDecl *Decl) {
+ if (!Decl->isCompleteDefinition())
+ return true;
+
+ // Skip C++ structs/classes/unions
+ // TODO: support C++ records
+ if (isa<CXXRecordDecl>(Decl))
+ return true;
+
+ if (!LocationChecker(Decl->getLocation()))
+ return true;
+
+ // Collect symbol information.
+ StringRef Name = Decl->getName();
+ if (Name.empty())
+ Name = getTypedefName(Decl);
+ if (Name.empty())
+ return true;
+
+ StringRef USR = API.recordUSR(Decl);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the struct.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ StructRecord *StructRecord =
+ API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
+ SubHeading, isInSystemHeader(Context, Decl));
+
+ // Now collect information about the fields in this struct.
+ recordStructFields(StructRecord, Decl->fields());
+
+ return true;
+}
+
+bool ExtractAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
+ // Skip forward declaration for classes (@class)
+ if (!Decl->isThisDeclarationADefinition())
+ return true;
+
+ if (!LocationChecker(Decl->getLocation()))
+ return true;
+
+ // Collect symbol information.
+ StringRef Name = Decl->getName();
+ StringRef USR = API.recordUSR(Decl);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ LinkageInfo Linkage = Decl->getLinkageAndVisibility();
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the interface.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ // Collect super class information.
+ SymbolReference SuperClass;
+ if (const auto *SuperClassDecl = Decl->getSuperClass()) {
+ SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
+ SuperClass.USR = API.recordUSR(SuperClassDecl);
+ }
+
+ ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface(
+ Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration,
+ SubHeading, SuperClass, isInSystemHeader(Context, Decl));
+
+ // Record all methods (selectors). This doesn't include automatically
+ // synthesized property methods.
+ recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
+ recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
+ recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
+ recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
+
+ return true;
+}
+
+bool ExtractAPIVisitor::VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
+ // Skip forward declaration for protocols (@protocol).
+ if (!Decl->isThisDeclarationADefinition())
+ return true;
+
+ if (!LocationChecker(Decl->getLocation()))
+ return true;
+
+ // Collect symbol information.
+ StringRef Name = Decl->getName();
+ StringRef USR = API.recordUSR(Decl);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the protocol.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
+ Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
+ isInSystemHeader(Context, Decl));
+
+ recordObjCMethods(ObjCProtocolRecord, Decl->methods());
+ recordObjCProperties(ObjCProtocolRecord, Decl->properties());
+ recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
+
+ return true;
+}
+
+bool ExtractAPIVisitor::VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
+ // Skip ObjC Type Parameter for now.
+ if (isa<ObjCTypeParamDecl>(Decl))
+ return true;
+
+ if (!Decl->isDefinedOutsideFunctionOrMethod())
+ return true;
+
+ if (!LocationChecker(Decl->getLocation()))
+ return true;
+
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ StringRef Name = Decl->getName();
+ StringRef USR = API.recordUSR(Decl);
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ QualType Type = Decl->getUnderlyingType();
+ SymbolReference SymRef =
+ TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
+ API);
+
+ API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment,
+ DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
+ DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef,
+ isInSystemHeader(Context, Decl));
+
+ return true;
+}
+
+bool ExtractAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
+ // Collect symbol information.
+ StringRef Name = Decl->getName();
+ StringRef USR = API.recordUSR(Decl);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Decl->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+ // Build declaration fragments and sub-heading for the category.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
+ SymbolReference Interface(InterfaceDecl->getName(),
+ API.recordUSR(InterfaceDecl));
+
+ ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory(
+ Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
+ Interface, isInSystemHeader(Context, Decl));
+
+ recordObjCMethods(ObjCCategoryRecord, Decl->methods());
+ recordObjCProperties(ObjCCategoryRecord, Decl->properties());
+ recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
+ recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
+
+ return true;
+}
+
+/// Collect API information for the enum constants and associate with the
+/// parent enum.
+void ExtractAPIVisitor::recordEnumConstants(
+ EnumRecord *EnumRecord, const EnumDecl::enumerator_range Constants) {
+ for (const auto *Constant : Constants) {
+ // Collect symbol information.
+ StringRef Name = Constant->getName();
+ StringRef USR = API.recordUSR(Constant);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Constant->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the enum constant.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Constant);
+
+ API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant),
+ Comment, Declaration, SubHeading,
+ isInSystemHeader(Context, Constant));
+ }
+}
+
+/// Collect API information for the struct fields and associate with the
+/// parent struct.
+void ExtractAPIVisitor::recordStructFields(
+ StructRecord *StructRecord, const RecordDecl::field_range Fields) {
+ for (const auto *Field : Fields) {
+ // Collect symbol information.
+ StringRef Name = Field->getName();
+ StringRef USR = API.recordUSR(Field);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Field->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the struct field.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForField(Field);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Field);
+
+ API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field),
+ Comment, Declaration, SubHeading,
+ isInSystemHeader(Context, Field));
+ }
+}
+
+/// Collect API information for the Objective-C methods and associate with the
+/// parent container.
+void ExtractAPIVisitor::recordObjCMethods(
+ ObjCContainerRecord *Container,
+ const ObjCContainerDecl::method_range Methods) {
+ for (const auto *Method : Methods) {
+ // Don't record selectors for properties.
+ if (Method->isPropertyAccessor())
+ continue;
+
+ StringRef Name = API.copyString(Method->getSelector().getAsString());
+ StringRef USR = API.recordUSR(Method);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Method->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments, sub-heading, and signature for the method.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Method);
+ FunctionSignature Signature =
+ DeclarationFragmentsBuilder::getFunctionSignature(Method);
+
+ API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method),
+ Comment, Declaration, SubHeading, Signature,
+ Method->isInstanceMethod(),
+ isInSystemHeader(Context, Method));
+ }
+}
+
+void ExtractAPIVisitor::recordObjCProperties(
+ ObjCContainerRecord *Container,
+ const ObjCContainerDecl::prop_range Properties) {
+ for (const auto *Property : Properties) {
+ StringRef Name = Property->getName();
+ StringRef USR = API.recordUSR(Property);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Property->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the property.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Property);
+
+ StringRef GetterName =
+ API.copyString(Property->getGetterName().getAsString());
+ StringRef SetterName =
+ API.copyString(Property->getSetterName().getAsString());
+
+ // Get the attributes for property.
+ unsigned Attributes = ObjCPropertyRecord::NoAttr;
+ if (Property->getPropertyAttributes() &
+ ObjCPropertyAttribute::kind_readonly)
+ Attributes |= ObjCPropertyRecord::ReadOnly;
+
+ API.addObjCProperty(
+ Container, Name, USR, Loc, AvailabilitySet(Property), Comment,
+ Declaration, SubHeading,
+ static_cast<ObjCPropertyRecord::AttributeKind>(Attributes), GetterName,
+ SetterName, Property->isOptional(),
+ !(Property->getPropertyAttributes() &
+ ObjCPropertyAttribute::kind_class),
+ isInSystemHeader(Context, Property));
+ }
+}
+
+void ExtractAPIVisitor::recordObjCInstanceVariables(
+ ObjCContainerRecord *Container,
+ const llvm::iterator_range<
+ DeclContext::specific_decl_iterator<ObjCIvarDecl>>
+ Ivars) {
+ for (const auto *Ivar : Ivars) {
+ StringRef Name = Ivar->getName();
+ StringRef USR = API.recordUSR(Ivar);
+ PresumedLoc Loc =
+ Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
+ DocComment Comment;
+ if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
+ Comment = RawComment->getFormattedLines(Context.getSourceManager(),
+ Context.getDiagnostics());
+
+ // Build declaration fragments and sub-heading for the instance variable.
+ DeclarationFragments Declaration =
+ DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Ivar);
+
+ ObjCInstanceVariableRecord::AccessControl Access =
+ Ivar->getCanonicalAccessControl();
+
+ API.addObjCInstanceVariable(
+ Container, Name, USR, Loc, AvailabilitySet(Ivar), Comment, Declaration,
+ SubHeading, Access, isInSystemHeader(Context, Ivar));
+ }
+}
+
+void ExtractAPIVisitor::recordObjCProtocols(
+ ObjCContainerRecord *Container,
+ ObjCInterfaceDecl::protocol_range Protocols) {
+ for (const auto *Protocol : Protocols)
+ Container->Protocols.emplace_back(Protocol->getName(),
+ API.recordUSR(Protocol));
+}
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 709b781968bf..01e9b37d2680 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -12,12 +12,21 @@
//===----------------------------------------------------------------------===//
#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Version.h"
#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/APIIgnoresList.h"
#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "clang/ExtractAPI/Serialization/SerializerBase.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VersionTuple.h"
+#include <optional>
#include <type_traits>
using namespace clang;
@@ -29,16 +38,16 @@ namespace {
/// Helper function to inject a JSON object \p Obj into another object \p Paren
/// at position \p Key.
-void serializeObject(Object &Paren, StringRef Key, Optional<Object> Obj) {
+void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) {
if (Obj)
- Paren[Key] = std::move(Obj.value());
+ Paren[Key] = std::move(*Obj);
}
/// Helper function to inject a JSON array \p Array into object \p Paren at
/// position \p Key.
-void serializeArray(Object &Paren, StringRef Key, Optional<Array> Array) {
+void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
if (Array)
- Paren[Key] = std::move(Array.value());
+ Paren[Key] = std::move(*Array);
}
/// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
@@ -55,11 +64,11 @@ void serializeArray(Object &Paren, StringRef Key, Optional<Array> Array) {
/// }
/// \endcode
///
-/// \returns \c None if the version \p V is empty, or an \c Object containing
-/// the semantic version representation of \p V.
-Optional<Object> serializeSemanticVersion(const VersionTuple &V) {
+/// \returns \c std::nullopt if the version \p V is empty, or an \c Object
+/// containing the semantic version representation of \p V.
+std::optional<Object> serializeSemanticVersion(const VersionTuple &V) {
if (V.empty())
- return None;
+ return std::nullopt;
Object Version;
Version["major"] = V.getMajor();
@@ -135,30 +144,43 @@ Object serializeSourceRange(const PresumedLoc &BeginLoc,
/// Serialize the availability attributes of a symbol.
///
/// Availability information contains the introduced, deprecated, and obsoleted
-/// versions of the symbol as semantic versions, if not default.
-/// Availability information also contains flags to indicate if the symbol is
-/// unconditionally unavailable or deprecated,
-/// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)).
+/// versions of the symbol for a given domain (roughly corresponds to a
+/// platform) as semantic versions, if not default. Availability information
+/// also contains flags to indicate if the symbol is unconditionally unavailable
+/// or deprecated, i.e. \c __attribute__((unavailable)) and \c
+/// __attribute__((deprecated)).
///
-/// \returns \c None if the symbol has default availability attributes, or
-/// an \c Object containing the formatted availability information.
-Optional<Object> serializeAvailability(const AvailabilityInfo &Avail) {
- if (Avail.isDefault())
- return None;
-
- Object Availbility;
- serializeObject(Availbility, "introducedVersion",
- serializeSemanticVersion(Avail.Introduced));
- serializeObject(Availbility, "deprecatedVersion",
- serializeSemanticVersion(Avail.Deprecated));
- serializeObject(Availbility, "obsoletedVersion",
- serializeSemanticVersion(Avail.Obsoleted));
- if (Avail.isUnavailable())
- Availbility["isUnconditionallyUnavailable"] = true;
- if (Avail.isUnconditionallyDeprecated())
- Availbility["isUnconditionallyDeprecated"] = true;
-
- return Availbility;
+/// \returns \c std::nullopt if the symbol has default availability attributes,
+/// or an \c Array containing the formatted availability information.
+std::optional<Array>
+serializeAvailability(const AvailabilitySet &Availabilities) {
+ if (Availabilities.isDefault())
+ return std::nullopt;
+
+ Array AvailabilityArray;
+
+ if (Availabilities.isUnconditionallyDeprecated()) {
+ Object UnconditionallyDeprecated;
+ UnconditionallyDeprecated["domain"] = "*";
+ UnconditionallyDeprecated["isUnconditionallyDeprecated"] = true;
+ AvailabilityArray.emplace_back(std::move(UnconditionallyDeprecated));
+ }
+
+ // Note unconditionally unavailable records are skipped.
+
+ for (const auto &AvailInfo : Availabilities) {
+ Object Availability;
+ Availability["domain"] = AvailInfo.Domain;
+ serializeObject(Availability, "introducedVersion",
+ serializeSemanticVersion(AvailInfo.Introduced));
+ serializeObject(Availability, "deprecatedVersion",
+ serializeSemanticVersion(AvailInfo.Deprecated));
+ serializeObject(Availability, "obsoletedVersion",
+ serializeSemanticVersion(AvailInfo.Obsoleted));
+ AvailabilityArray.emplace_back(std::move(Availability));
+ }
+
+ return AvailabilityArray;
}
/// Get the language name string for interface language references.
@@ -215,11 +237,11 @@ Object serializeIdentifier(const APIRecord &Record, Language Lang) {
/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line.
/// \endcode
///
-/// \returns \c None if \p Comment is empty, or an \c Object containing the
-/// formatted lines.
-Optional<Object> serializeDocComment(const DocComment &Comment) {
+/// \returns \c std::nullopt if \p Comment is empty, or an \c Object containing
+/// the formatted lines.
+std::optional<Object> serializeDocComment(const DocComment &Comment) {
if (Comment.empty())
- return None;
+ return std::nullopt;
Object DocComment;
Array LinesArray;
@@ -267,11 +289,12 @@ Optional<Object> serializeDocComment(const DocComment &Comment) {
/// ]
/// \endcode
///
-/// \returns \c None if \p DF is empty, or an \c Array containing the formatted
-/// declaration fragments array.
-Optional<Array> serializeDeclarationFragments(const DeclarationFragments &DF) {
+/// \returns \c std::nullopt if \p DF is empty, or an \c Array containing the
+/// formatted declaration fragments array.
+std::optional<Array>
+serializeDeclarationFragments(const DeclarationFragments &DF) {
if (DF.getFragments().empty())
- return None;
+ return std::nullopt;
Array Fragments;
for (const auto &F : DF.getFragments()) {
@@ -310,18 +333,16 @@ Object serializeNames(const APIRecord &Record) {
return Names;
}
-/// Serialize the symbol kind information.
-///
-/// The Symbol Graph symbol kind property contains a shorthand \c identifier
-/// which is prefixed by the source language name, useful for tooling to parse
-/// the kind, and a \c displayName for rendering human-readable names.
-Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
+Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
return (getLanguageName(Lang) + "." + S).str();
};
Object Kind;
- switch (Record.getKind()) {
+ switch (RK) {
+ case APIRecord::RK_Unknown:
+ llvm_unreachable("Records should have an explicit kind");
+ break;
case APIRecord::RK_GlobalFunction:
Kind["identifier"] = AddLangPrefix("func");
Kind["displayName"] = "Function";
@@ -350,19 +371,22 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
Kind["identifier"] = AddLangPrefix("ivar");
Kind["displayName"] = "Instance Variable";
break;
- case APIRecord::RK_ObjCMethod:
- if (dyn_cast<ObjCMethodRecord>(&Record)->IsInstanceMethod) {
- Kind["identifier"] = AddLangPrefix("method");
- Kind["displayName"] = "Instance Method";
- } else {
- Kind["identifier"] = AddLangPrefix("type.method");
- Kind["displayName"] = "Type Method";
- }
+ case APIRecord::RK_ObjCInstanceMethod:
+ Kind["identifier"] = AddLangPrefix("method");
+ Kind["displayName"] = "Instance Method";
break;
- case APIRecord::RK_ObjCProperty:
+ case APIRecord::RK_ObjCClassMethod:
+ Kind["identifier"] = AddLangPrefix("type.method");
+ Kind["displayName"] = "Type Method";
+ break;
+ case APIRecord::RK_ObjCInstanceProperty:
Kind["identifier"] = AddLangPrefix("property");
Kind["displayName"] = "Instance Property";
break;
+ case APIRecord::RK_ObjCClassProperty:
+ Kind["identifier"] = AddLangPrefix("type.property");
+ Kind["displayName"] = "Type Property";
+ break;
case APIRecord::RK_ObjCInterface:
Kind["identifier"] = AddLangPrefix("class");
Kind["displayName"] = "Class";
@@ -389,12 +413,21 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
return Kind;
}
+/// Serialize the symbol kind information.
+///
+/// The Symbol Graph symbol kind property contains a shorthand \c identifier
+/// which is prefixed by the source language name, useful for tooling to parse
+/// the kind, and a \c displayName for rendering human-readable names.
+Object serializeSymbolKind(const APIRecord &Record, Language Lang) {
+ return serializeSymbolKind(Record.getKind(), Lang);
+}
+
template <typename RecordTy>
-Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
- std::true_type) {
+std::optional<Object>
+serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) {
const auto &FS = Record.Signature;
if (FS.empty())
- return None;
+ return std::nullopt;
Object Signature;
serializeArray(Signature, "returns",
@@ -416,9 +449,9 @@ Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
}
template <typename RecordTy>
-Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
- std::false_type) {
- return None;
+std::optional<Object>
+serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::false_type) {
+ return std::nullopt;
}
/// Serialize the function signature field, as specified by the
@@ -429,7 +462,7 @@ Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
/// - The \c parameters array contains names and declaration fragments of the
/// parameters.
///
-/// \returns \c None if \p FS is empty, or an \c Object containing the
+/// \returns \c std::nullopt if \p FS is empty, or an \c Object containing the
/// formatted function signature.
template <typename RecordTy>
void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
@@ -438,6 +471,78 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
Record, has_function_signature<RecordTy>()));
}
+struct PathComponent {
+ StringRef USR;
+ StringRef Name;
+ APIRecord::RecordKind Kind;
+
+ PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
+ : USR(USR), Name(Name), Kind(Kind) {}
+};
+
+template <typename RecordTy>
+bool generatePathComponents(
+ const RecordTy &Record, const APISet &API,
+ function_ref<void(const PathComponent &)> ComponentTransformer) {
+ SmallVector<PathComponent, 4> ReverseComponenents;
+ ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind());
+ const auto *CurrentParent = &Record.ParentInformation;
+ while (CurrentParent && !CurrentParent->empty()) {
+ PathComponent CurrentParentComponent(CurrentParent->ParentUSR,
+ CurrentParent->ParentName,
+ CurrentParent->ParentKind);
+
+ auto *ParentRecord = CurrentParent->ParentRecord;
+ // Slow path if we don't have a direct reference to the ParentRecord
+ if (!ParentRecord)
+ ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR);
+
+ // If the parent is a category then we need to pretend this belongs to the
+ // associated interface.
+ if (auto *CategoryRecord =
+ dyn_cast_or_null<ObjCCategoryRecord>(ParentRecord)) {
+ ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR);
+ CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR,
+ CategoryRecord->Interface.Name,
+ APIRecord::RK_ObjCInterface);
+ }
+
+ // The parent record doesn't exist which means the symbol shouldn't be
+ // treated as part of the current product.
+ if (!ParentRecord)
+ return true;
+
+ ReverseComponenents.push_back(std::move(CurrentParentComponent));
+ CurrentParent = &ParentRecord->ParentInformation;
+ }
+
+ for (const auto &PC : reverse(ReverseComponenents))
+ ComponentTransformer(PC);
+
+ return false;
+}
+Object serializeParentContext(const PathComponent &PC, Language Lang) {
+ Object ParentContextElem;
+ ParentContextElem["usr"] = PC.USR;
+ ParentContextElem["name"] = PC.Name;
+ ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"];
+ return ParentContextElem;
+}
+
+template <typename RecordTy>
+Array generateParentContexts(const RecordTy &Record, const APISet &API,
+ Language Lang) {
+ Array ParentContexts;
+ if (generatePathComponents(
+ Record, API, [Lang, &ParentContexts](const PathComponent &PC) {
+ ParentContexts.push_back(serializeParentContext(PC, Lang));
+ }))
+ ParentContexts.clear();
+ ParentContexts.pop_back();
+
+ return ParentContexts;
+}
+
} // namespace
void SymbolGraphSerializer::anchor() {}
@@ -457,14 +562,18 @@ Object SymbolGraphSerializer::serializeModule() const {
Object Module;
// The user is expected to always pass `--product-name=` on the command line
// to populate this field.
- Module["name"] = ProductName;
+ Module["name"] = API.ProductName;
serializeObject(Module, "platform", serializePlatform(API.getTarget()));
return Module;
}
bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
+ // Skip explicitly ignored symbols.
+ if (IgnoresList.shouldIgnore(Record.Name))
+ return true;
+
// Skip unconditionally unavailable symbols
- if (Record.Availability.isUnconditionallyUnavailable())
+ if (Record.Availabilities.isUnconditionallyUnavailable())
return true;
// Filter out symbols prefixed with an underscored as they are understood to
@@ -476,10 +585,10 @@ bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
}
template <typename RecordTy>
-Optional<Object>
+std::optional<Object>
SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
if (shouldSkip(Record))
- return None;
+ return std::nullopt;
Object Obj;
serializeObject(Obj, "identifier",
@@ -489,15 +598,24 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
serializeObject(
Obj, "location",
serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
- serializeObject(Obj, "availbility",
- serializeAvailability(Record.Availability));
+ serializeArray(Obj, "availability",
+ serializeAvailability(Record.Availabilities));
serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
serializeArray(Obj, "declarationFragments",
serializeDeclarationFragments(Record.Declaration));
// TODO: Once we keep track of symbol access information serialize it
// correctly here.
Obj["accessLevel"] = "public";
- serializeArray(Obj, "pathComponents", Array(PathComponents));
+ SmallVector<StringRef, 4> PathComponentsNames;
+ // If this returns true it indicates that we couldn't find a symbol in the
+ // hierarchy.
+ if (generatePathComponents(Record, API,
+ [&PathComponentsNames](const PathComponent &PC) {
+ PathComponentsNames.push_back(PC.Name);
+ }))
+ return {};
+
+ serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
serializeFunctionSignatureMixin(Obj, Record);
@@ -508,8 +626,10 @@ template <typename MemberTy>
void SymbolGraphSerializer::serializeMembers(
const APIRecord &Record,
const SmallVector<std::unique_ptr<MemberTy>> &Members) {
+ // Members should not be serialized if we aren't recursing.
+ if (!ShouldRecurse)
+ return;
for (const auto &Member : Members) {
- auto MemberPathComponentGuard = makePathComponentGuard(Member->Name);
auto MemberRecord = serializeAPIRecord(*Member);
if (!MemberRecord)
continue;
@@ -537,6 +657,7 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
Object Relationship;
Relationship["source"] = Source.USR;
Relationship["target"] = Target.USR;
+ Relationship["targetFallback"] = Target.Name;
Relationship["kind"] = getRelationshipString(Kind);
Relationships.emplace_back(std::move(Relationship));
@@ -544,8 +665,6 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
void SymbolGraphSerializer::serializeGlobalFunctionRecord(
const GlobalFunctionRecord &Record) {
- auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
auto Obj = serializeAPIRecord(Record);
if (!Obj)
return;
@@ -555,8 +674,6 @@ void SymbolGraphSerializer::serializeGlobalFunctionRecord(
void SymbolGraphSerializer::serializeGlobalVariableRecord(
const GlobalVariableRecord &Record) {
- auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
auto Obj = serializeAPIRecord(Record);
if (!Obj)
return;
@@ -565,7 +682,6 @@ void SymbolGraphSerializer::serializeGlobalVariableRecord(
}
void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
- auto EnumPathComponentGuard = makePathComponentGuard(Record.Name);
auto Enum = serializeAPIRecord(Record);
if (!Enum)
return;
@@ -575,7 +691,6 @@ void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
}
void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
- auto StructPathComponentGuard = makePathComponentGuard(Record.Name);
auto Struct = serializeAPIRecord(Record);
if (!Struct)
return;
@@ -586,7 +701,6 @@ void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
void SymbolGraphSerializer::serializeObjCContainerRecord(
const ObjCContainerRecord &Record) {
- auto ObjCContainerPathComponentGuard = makePathComponentGuard(Record.Name);
auto ObjCContainer = serializeAPIRecord(Record);
if (!ObjCContainer)
return;
@@ -615,7 +729,7 @@ void SymbolGraphSerializer::serializeObjCContainerRecord(
serializeMembers(Record, Category->Methods);
serializeMembers(Record, Category->Properties);
- // Surface the protocols of the the category to the interface.
+ // Surface the protocols of the category to the interface.
for (const auto &Protocol : Category->Protocols)
serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
}
@@ -624,7 +738,6 @@ void SymbolGraphSerializer::serializeObjCContainerRecord(
void SymbolGraphSerializer::serializeMacroDefinitionRecord(
const MacroDefinitionRecord &Record) {
- auto MacroPathComponentGuard = makePathComponentGuard(Record.Name);
auto Macro = serializeAPIRecord(Record);
if (!Macro)
@@ -633,6 +746,46 @@ void SymbolGraphSerializer::serializeMacroDefinitionRecord(
Symbols.emplace_back(std::move(*Macro));
}
+void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
+ switch (Record->getKind()) {
+ case APIRecord::RK_Unknown:
+ llvm_unreachable("Records should have a known kind!");
+ case APIRecord::RK_GlobalFunction:
+ serializeGlobalFunctionRecord(*cast<GlobalFunctionRecord>(Record));
+ break;
+ case APIRecord::RK_GlobalVariable:
+ serializeGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
+ break;
+ case APIRecord::RK_Enum:
+ serializeEnumRecord(*cast<EnumRecord>(Record));
+ break;
+ case APIRecord::RK_Struct:
+ serializeStructRecord(*cast<StructRecord>(Record));
+ break;
+ case APIRecord::RK_ObjCInterface:
+ serializeObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
+ break;
+ case APIRecord::RK_ObjCProtocol:
+ serializeObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
+ break;
+ case APIRecord::RK_MacroDefinition:
+ serializeMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
+ break;
+ case APIRecord::RK_Typedef:
+ serializeTypedefRecord(*cast<TypedefRecord>(Record));
+ break;
+ default:
+ if (auto Obj = serializeAPIRecord(*Record)) {
+ Symbols.emplace_back(std::move(*Obj));
+ auto &ParentInformation = Record->ParentInformation;
+ if (!ParentInformation.empty())
+ serializeRelationship(RelationshipKind::MemberOf, *Record,
+ *ParentInformation.ParentRecord);
+ }
+ break;
+ }
+}
+
void SymbolGraphSerializer::serializeTypedefRecord(
const TypedefRecord &Record) {
// Typedefs of anonymous types have their entries unified with the underlying
@@ -644,7 +797,6 @@ void SymbolGraphSerializer::serializeTypedefRecord(
if (ShouldDrop)
return;
- auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name);
auto Typedef = serializeAPIRecord(Record);
if (!Typedef)
return;
@@ -654,16 +806,7 @@ void SymbolGraphSerializer::serializeTypedefRecord(
Symbols.emplace_back(std::move(*Typedef));
}
-SymbolGraphSerializer::PathComponentGuard
-SymbolGraphSerializer::makePathComponentGuard(StringRef Component) {
- return PathComponentGuard(PathComponents, Component);
-}
-
Object SymbolGraphSerializer::serialize() {
- Object Root;
- serializeObject(Root, "metadata", serializeMetadata());
- serializeObject(Root, "module", serializeModule());
-
// Serialize global variables in the API set.
for (const auto &GlobalVar : API.getGlobalVariables())
serializeGlobalVariableRecord(*GlobalVar.second);
@@ -693,6 +836,14 @@ Object SymbolGraphSerializer::serialize() {
for (const auto &Typedef : API.getTypedefs())
serializeTypedefRecord(*Typedef.second);
+ return serializeCurrentGraph();
+}
+
+Object SymbolGraphSerializer::serializeCurrentGraph() {
+ Object Root;
+ serializeObject(Root, "metadata", serializeMetadata());
+ serializeObject(Root, "module", serializeModule());
+
Root["symbols"] = std::move(Symbols);
Root["relationships"] = std::move(Relationships);
@@ -706,3 +857,53 @@ void SymbolGraphSerializer::serialize(raw_ostream &os) {
else
os << formatv("{0:2}", Value(std::move(root))) << "\n";
}
+
+std::optional<Object>
+SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR,
+ const APISet &API) {
+ APIRecord *Record = API.findRecordForUSR(USR);
+ if (!Record)
+ return {};
+
+ Object Root;
+ APIIgnoresList EmptyIgnores;
+ SymbolGraphSerializer Serializer(API, EmptyIgnores,
+ /*Options.Compact*/ {true},
+ /*ShouldRecurse*/ false);
+ Serializer.serializeSingleRecord(Record);
+ serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph());
+
+ Language Lang = API.getLanguage();
+ serializeArray(Root, "parentContexts",
+ generateParentContexts(*Record, API, Lang));
+
+ Array RelatedSymbols;
+
+ for (const auto &Fragment : Record->Declaration.getFragments()) {
+ // If we don't have a USR there isn't much we can do.
+ if (Fragment.PreciseIdentifier.empty())
+ continue;
+
+ APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier);
+
+ // If we can't find the record let's skip.
+ if (!RelatedRecord)
+ continue;
+
+ Object RelatedSymbol;
+ RelatedSymbol["usr"] = RelatedRecord->USR;
+ RelatedSymbol["declarationLanguage"] = getLanguageName(Lang);
+ // TODO: once we record this properly let's serialize it right.
+ RelatedSymbol["accessLevel"] = "public";
+ RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename();
+ RelatedSymbol["moduleName"] = API.ProductName;
+ RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader;
+
+ serializeArray(RelatedSymbol, "parentContexts",
+ generateParentContexts(*RelatedRecord, API, Lang));
+ RelatedSymbols.push_back(std::move(RelatedSymbol));
+ }
+
+ serializeArray(Root, "relatedSymbols", RelatedSymbols);
+ return Root;
+}
diff --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
index 9c165e693e0e..3da2424ea726 100644
--- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
@@ -17,9 +17,8 @@
using namespace clang;
using namespace extractapi;
-namespace {
-
-const NamedDecl *getUnderlyingTypeDecl(QualType Type) {
+const NamedDecl *
+TypedefUnderlyingTypeResolver::getUnderlyingTypeDecl(QualType Type) const {
const NamedDecl *TypeDecl = nullptr;
const TypedefType *TypedefTy = Type->getAs<TypedefType>();
@@ -44,8 +43,6 @@ const NamedDecl *getUnderlyingTypeDecl(QualType Type) {
return TypeDecl;
}
-} // namespace
-
SymbolReference
TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type,
APISet &API) const {
diff --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
index 0096ff235914..54aa11c354c0 100644
--- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
+++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
@@ -26,6 +26,8 @@ namespace clang {
namespace extractapi {
struct TypedefUnderlyingTypeResolver {
+ /// Gets the underlying type declaration.
+ const NamedDecl *getUnderlyingTypeDecl(QualType Type) const;
/// Get a SymbolReference for the given type.
SymbolReference getSymbolReferenceForType(QualType Type, APISet &API) const;