aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/ExtractAPI
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/ExtractAPI')
-rw-r--r--contrib/llvm-project/clang/lib/ExtractAPI/API.cpp233
-rw-r--r--contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp799
-rw-r--r--contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp974
-rw-r--r--contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SerializerBase.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp708
-rw-r--r--contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp79
-rw-r--r--contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h46
7 files changed, 2858 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/API.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/API.cpp
new file mode 100644
index 000000000000..073e36f320d3
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/ExtractAPI/API.cpp
@@ -0,0 +1,233 @@
+//===- ExtractAPI/API.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 APIRecord and derived record structs,
+/// and the APISet class.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ExtractAPI/API.h"
+#include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/CommentLexer.h"
+#include "clang/AST/RawCommentList.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+
+using namespace clang::extractapi;
+using namespace llvm;
+
+namespace {
+
+template <typename RecordTy, typename... CtorArgsTy>
+RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
+ StringRef USR, CtorArgsTy &&...CtorArgs) {
+ auto Result = RecordMap.insert({USR, nullptr});
+
+ // Create the record if it does not already exist
+ if (Result.second)
+ Result.first->second =
+ std::make_unique<RecordTy>(USR, std::forward<CtorArgsTy>(CtorArgs)...);
+
+ return Result.first->second.get();
+}
+
+} // namespace
+
+GlobalVariableRecord *
+APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc,
+ const AvailabilityInfo &Availability, LinkageInfo Linkage,
+ const DocComment &Comment, DeclarationFragments Fragments,
+ DeclarationFragments SubHeading) {
+ return addTopLevelRecord(GlobalVariables, USR, Name, Loc, Availability,
+ Linkage, Comment, Fragments, SubHeading);
+}
+
+GlobalFunctionRecord *APISet::addGlobalFunction(
+ StringRef Name, StringRef USR, PresumedLoc Loc,
+ const AvailabilityInfo &Availability, LinkageInfo Linkage,
+ const DocComment &Comment, DeclarationFragments Fragments,
+ DeclarationFragments SubHeading, FunctionSignature Signature) {
+ return addTopLevelRecord(GlobalFunctions, USR, Name, Loc, Availability,
+ Linkage, Comment, Fragments, SubHeading, Signature);
+}
+
+EnumConstantRecord *APISet::addEnumConstant(
+ EnumRecord *Enum, StringRef Name, StringRef USR, PresumedLoc Loc,
+ const AvailabilityInfo &Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading) {
+ auto Record = std::make_unique<EnumConstantRecord>(
+ USR, Name, Loc, Availability, Comment, Declaration, SubHeading);
+ return Enum->Constants.emplace_back(std::move(Record)).get();
+}
+
+EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc,
+ const AvailabilityInfo &Availability,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading) {
+ return addTopLevelRecord(Enums, USR, Name, Loc, Availability, Comment,
+ Declaration, SubHeading);
+}
+
+StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name,
+ StringRef USR, PresumedLoc Loc,
+ const AvailabilityInfo &Availability,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading) {
+ auto Record = std::make_unique<StructFieldRecord>(
+ USR, Name, Loc, Availability, Comment, Declaration, SubHeading);
+ return Struct->Fields.emplace_back(std::move(Record)).get();
+}
+
+StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
+ const AvailabilityInfo &Availability,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading) {
+ return addTopLevelRecord(Structs, USR, Name, Loc, Availability, Comment,
+ Declaration, SubHeading);
+}
+
+ObjCCategoryRecord *APISet::addObjCCategory(
+ StringRef Name, StringRef USR, PresumedLoc Loc,
+ const AvailabilityInfo &Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ SymbolReference Interface) {
+ // Create the category record.
+ auto *Record = addTopLevelRecord(ObjCCategories, USR, Name, Loc, Availability,
+ Comment, Declaration, SubHeading, Interface);
+
+ // If this category is extending a known interface, associate it with the
+ // ObjCInterfaceRecord.
+ auto It = ObjCInterfaces.find(Interface.USR);
+ if (It != ObjCInterfaces.end())
+ It->second->Categories.push_back(Record);
+
+ 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);
+}
+
+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);
+ 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,
+ 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);
+ 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) {
+ auto Record = std::make_unique<ObjCInstanceVariableRecord>(
+ USR, Name, Loc, Availability, Comment, Declaration, SubHeading, Access);
+ 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);
+}
+
+MacroDefinitionRecord *
+APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading) {
+ return addTopLevelRecord(Macros, USR, Name, Loc, Declaration, SubHeading);
+}
+
+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);
+}
+
+StringRef APISet::recordUSR(const Decl *D) {
+ SmallString<128> USR;
+ index::generateUSRForDecl(D, USR);
+ return copyString(USR);
+}
+
+StringRef APISet::recordUSRForMacro(StringRef Name, SourceLocation SL,
+ const SourceManager &SM) {
+ SmallString<128> USR;
+ index::generateUSRForMacro(Name, SL, SM, USR);
+ return copyString(USR);
+}
+
+StringRef APISet::copyString(StringRef String) {
+ if (String.empty())
+ return {};
+
+ // No need to allocate memory and copy if the string has already been stored.
+ if (StringAllocator.identifyObject(String.data()))
+ return String;
+
+ void *Ptr = StringAllocator.Allocate(String.size(), 1);
+ memcpy(Ptr, String.data(), String.size());
+ return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
+}
+
+APIRecord::~APIRecord() {}
+
+ObjCContainerRecord::~ObjCContainerRecord() {}
+
+void GlobalFunctionRecord::anchor() {}
+void GlobalVariableRecord::anchor() {}
+void EnumConstantRecord::anchor() {}
+void EnumRecord::anchor() {}
+void StructFieldRecord::anchor() {}
+void StructRecord::anchor() {}
+void ObjCPropertyRecord::anchor() {}
+void ObjCInstanceVariableRecord::anchor() {}
+void ObjCMethodRecord::anchor() {}
+void ObjCCategoryRecord::anchor() {}
+void ObjCInterfaceRecord::anchor() {}
+void ObjCProtocolRecord::anchor() {}
+void MacroDefinitionRecord::anchor() {}
+void TypedefRecord::anchor() {}
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp
new file mode 100644
index 000000000000..75d360a3ba16
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -0,0 +1,799 @@
+//===- ExtractAPI/DeclarationFragments.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 Declaration Fragments related classes.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "TypedefUnderlyingTypeResolver.h"
+#include "clang/Index/USRGeneration.h"
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace clang::extractapi;
+using namespace llvm;
+
+DeclarationFragments &DeclarationFragments::appendSpace() {
+ if (!Fragments.empty()) {
+ Fragment &Last = Fragments.back();
+ if (Last.Kind == FragmentKind::Text) {
+ // Merge the extra space into the last fragment if the last fragment is
+ // also text.
+ if (Last.Spelling.back() != ' ') { // avoid extra trailing spaces.
+ Last.Spelling.push_back(' ');
+ }
+ } else {
+ append(" ", FragmentKind::Text);
+ }
+ }
+
+ return *this;
+}
+
+StringRef DeclarationFragments::getFragmentKindString(
+ DeclarationFragments::FragmentKind Kind) {
+ switch (Kind) {
+ case DeclarationFragments::FragmentKind::None:
+ return "none";
+ case DeclarationFragments::FragmentKind::Keyword:
+ return "keyword";
+ case DeclarationFragments::FragmentKind::Attribute:
+ return "attribute";
+ case DeclarationFragments::FragmentKind::NumberLiteral:
+ return "number";
+ case DeclarationFragments::FragmentKind::StringLiteral:
+ return "string";
+ case DeclarationFragments::FragmentKind::Identifier:
+ return "identifier";
+ case DeclarationFragments::FragmentKind::TypeIdentifier:
+ return "typeIdentifier";
+ case DeclarationFragments::FragmentKind::GenericParameter:
+ return "genericParameter";
+ case DeclarationFragments::FragmentKind::ExternalParam:
+ return "externalParam";
+ case DeclarationFragments::FragmentKind::InternalParam:
+ return "internalParam";
+ case DeclarationFragments::FragmentKind::Text:
+ return "text";
+ }
+
+ llvm_unreachable("Unhandled FragmentKind");
+}
+
+DeclarationFragments::FragmentKind
+DeclarationFragments::parseFragmentKindFromString(StringRef S) {
+ return llvm::StringSwitch<FragmentKind>(S)
+ .Case("keyword", DeclarationFragments::FragmentKind::Keyword)
+ .Case("attribute", DeclarationFragments::FragmentKind::Attribute)
+ .Case("number", DeclarationFragments::FragmentKind::NumberLiteral)
+ .Case("string", DeclarationFragments::FragmentKind::StringLiteral)
+ .Case("identifier", DeclarationFragments::FragmentKind::Identifier)
+ .Case("typeIdentifier",
+ DeclarationFragments::FragmentKind::TypeIdentifier)
+ .Case("genericParameter",
+ DeclarationFragments::FragmentKind::GenericParameter)
+ .Case("internalParam", DeclarationFragments::FragmentKind::InternalParam)
+ .Case("externalParam", DeclarationFragments::FragmentKind::ExternalParam)
+ .Case("text", DeclarationFragments::FragmentKind::Text)
+ .Default(DeclarationFragments::FragmentKind::None);
+}
+
+// NNS stores C++ nested name specifiers, which are prefixes to qualified names.
+// Build declaration fragments for NNS recursively so that we have the USR for
+// every part in a qualified name, and also leaves the actual underlying type
+// cleaner for its own fragment.
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS,
+ ASTContext &Context,
+ DeclarationFragments &After) {
+ DeclarationFragments Fragments;
+ if (NNS->getPrefix())
+ Fragments.append(getFragmentsForNNS(NNS->getPrefix(), Context, After));
+
+ switch (NNS->getKind()) {
+ case NestedNameSpecifier::Identifier:
+ Fragments.append(NNS->getAsIdentifier()->getName(),
+ DeclarationFragments::FragmentKind::Identifier);
+ break;
+
+ case NestedNameSpecifier::Namespace: {
+ const NamespaceDecl *NS = NNS->getAsNamespace();
+ if (NS->isAnonymousNamespace())
+ return Fragments;
+ SmallString<128> USR;
+ index::generateUSRForDecl(NS, USR);
+ Fragments.append(NS->getName(),
+ DeclarationFragments::FragmentKind::Identifier, USR);
+ break;
+ }
+
+ case NestedNameSpecifier::NamespaceAlias: {
+ const NamespaceAliasDecl *Alias = NNS->getAsNamespaceAlias();
+ SmallString<128> USR;
+ index::generateUSRForDecl(Alias, USR);
+ Fragments.append(Alias->getName(),
+ DeclarationFragments::FragmentKind::Identifier, USR);
+ break;
+ }
+
+ case NestedNameSpecifier::Global:
+ // The global specifier `::` at the beginning. No stored value.
+ break;
+
+ case NestedNameSpecifier::Super:
+ // Microsoft's `__super` specifier.
+ Fragments.append("__super", DeclarationFragments::FragmentKind::Keyword);
+ break;
+
+ case NestedNameSpecifier::TypeSpecWithTemplate:
+ // A type prefixed by the `template` keyword.
+ Fragments.append("template", DeclarationFragments::FragmentKind::Keyword);
+ Fragments.appendSpace();
+ // Fallthrough after adding the keyword to handle the actual type.
+ LLVM_FALLTHROUGH;
+
+ case NestedNameSpecifier::TypeSpec: {
+ const Type *T = NNS->getAsType();
+ // FIXME: Handle C++ template specialization type
+ Fragments.append(getFragmentsForType(T, Context, After));
+ break;
+ }
+ }
+
+ // Add the separator text `::` for this segment.
+ return Fragments.append("::", DeclarationFragments::FragmentKind::Text);
+}
+
+// Recursively build the declaration fragments for an underlying `Type` with
+// qualifiers removed.
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
+ const Type *T, ASTContext &Context, DeclarationFragments &After) {
+ assert(T && "invalid type");
+
+ DeclarationFragments Fragments;
+
+ // Declaration fragments of a pointer type is the declaration fragments of
+ // the pointee type followed by a `*`, except for Objective-C `id` and `Class`
+ // pointers, where we do not spell out the `*`.
+ if (T->isPointerType() ||
+ (T->isObjCObjectPointerType() &&
+ !T->getAs<ObjCObjectPointerType>()->isObjCIdOrClassType())) {
+ return Fragments
+ .append(getFragmentsForType(T->getPointeeType(), Context, After))
+ .append(" *", DeclarationFragments::FragmentKind::Text);
+ }
+
+ // Declaration fragments of a lvalue reference type is the declaration
+ // fragments of the underlying type followed by a `&`.
+ if (const LValueReferenceType *LRT = dyn_cast<LValueReferenceType>(T))
+ return Fragments
+ .append(
+ getFragmentsForType(LRT->getPointeeTypeAsWritten(), Context, After))
+ .append(" &", DeclarationFragments::FragmentKind::Text);
+
+ // Declaration fragments of a rvalue reference type is the declaration
+ // fragments of the underlying type followed by a `&&`.
+ if (const RValueReferenceType *RRT = dyn_cast<RValueReferenceType>(T))
+ return Fragments
+ .append(
+ getFragmentsForType(RRT->getPointeeTypeAsWritten(), Context, After))
+ .append(" &&", DeclarationFragments::FragmentKind::Text);
+
+ // Declaration fragments of an array-typed variable have two parts:
+ // 1. the element type of the array that appears before the variable name;
+ // 2. array brackets `[(0-9)?]` that appear after the variable name.
+ if (const ArrayType *AT = T->getAsArrayTypeUnsafe()) {
+ // Build the "after" part first because the inner element type might also
+ // be an array-type. For example `int matrix[3][4]` which has a type of
+ // "(array 3 of (array 4 of ints))."
+ // Push the array size part first to make sure they are in the right order.
+ After.append("[", DeclarationFragments::FragmentKind::Text);
+
+ switch (AT->getSizeModifier()) {
+ case ArrayType::Normal:
+ break;
+ case ArrayType::Static:
+ Fragments.append("static", DeclarationFragments::FragmentKind::Keyword);
+ break;
+ case ArrayType::Star:
+ Fragments.append("*", DeclarationFragments::FragmentKind::Text);
+ break;
+ }
+
+ if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) {
+ // FIXME: right now this would evaluate any expressions/macros written in
+ // the original source to concrete values. For example
+ // `int nums[MAX]` -> `int nums[100]`
+ // `char *str[5 + 1]` -> `char *str[6]`
+ SmallString<128> Size;
+ CAT->getSize().toStringUnsigned(Size);
+ After.append(Size, DeclarationFragments::FragmentKind::NumberLiteral);
+ }
+
+ After.append("]", DeclarationFragments::FragmentKind::Text);
+
+ return Fragments.append(
+ getFragmentsForType(AT->getElementType(), Context, After));
+ }
+
+ // An ElaboratedType is a sugar for types that are referred to using an
+ // elaborated keyword, e.g., `struct S`, `enum E`, or (in C++) via a
+ // qualified name, e.g., `N::M::type`, or both.
+ if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(T)) {
+ ElaboratedTypeKeyword Keyword = ET->getKeyword();
+ if (Keyword != ETK_None) {
+ Fragments
+ .append(ElaboratedType::getKeywordName(Keyword),
+ DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+ }
+
+ if (const NestedNameSpecifier *NNS = ET->getQualifier())
+ Fragments.append(getFragmentsForNNS(NNS, Context, After));
+
+ // After handling the elaborated keyword or qualified name, build
+ // declaration fragments for the desugared underlying type.
+ return Fragments.append(getFragmentsForType(ET->desugar(), Context, After));
+ }
+
+ // Everything we care about has been handled now, reduce to the canonical
+ // unqualified base type.
+ QualType Base = T->getCanonicalTypeUnqualified();
+
+ // Render Objective-C `id`/`instancetype` as keywords.
+ if (T->isObjCIdType())
+ return Fragments.append(Base.getAsString(),
+ DeclarationFragments::FragmentKind::Keyword);
+
+ // If the type is a typedefed type, get the underlying TypedefNameDecl for a
+ // 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);
+ }
+
+ // If the base type is a TagType (struct/interface/union/class/enum), let's
+ // get the underlying Decl for better names and USRs.
+ if (const TagType *TagTy = dyn_cast<TagType>(Base)) {
+ const TagDecl *Decl = TagTy->getDecl();
+ // Anonymous decl, skip this fragment.
+ if (Decl->getName().empty())
+ return Fragments;
+ SmallString<128> TagUSR;
+ clang::index::generateUSRForDecl(Decl, TagUSR);
+ return Fragments.append(Decl->getName(),
+ DeclarationFragments::FragmentKind::TypeIdentifier,
+ TagUSR);
+ }
+
+ // If the base type is an ObjCInterfaceType, use the underlying
+ // ObjCInterfaceDecl for the true USR.
+ if (const auto *ObjCIT = dyn_cast<ObjCInterfaceType>(Base)) {
+ const auto *Decl = ObjCIT->getDecl();
+ SmallString<128> USR;
+ index::generateUSRForDecl(Decl, USR);
+ return Fragments.append(Decl->getName(),
+ DeclarationFragments::FragmentKind::TypeIdentifier,
+ USR);
+ }
+
+ // Default fragment builder for other kinds of types (BuiltinType etc.)
+ SmallString<128> USR;
+ clang::index::generateUSRForType(Base, Context, USR);
+ Fragments.append(Base.getAsString(),
+ DeclarationFragments::FragmentKind::TypeIdentifier, USR);
+
+ return Fragments;
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForQualifiers(const Qualifiers Quals) {
+ DeclarationFragments Fragments;
+ if (Quals.hasConst())
+ Fragments.append("const", DeclarationFragments::FragmentKind::Keyword);
+ if (Quals.hasVolatile())
+ Fragments.append("volatile", DeclarationFragments::FragmentKind::Keyword);
+ if (Quals.hasRestrict())
+ Fragments.append("restrict", DeclarationFragments::FragmentKind::Keyword);
+
+ return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
+ const QualType QT, ASTContext &Context, DeclarationFragments &After) {
+ assert(!QT.isNull() && "invalid type");
+
+ if (const ParenType *PT = dyn_cast<ParenType>(QT)) {
+ After.append(")", DeclarationFragments::FragmentKind::Text);
+ return getFragmentsForType(PT->getInnerType(), Context, After)
+ .append("(", DeclarationFragments::FragmentKind::Text);
+ }
+
+ const SplitQualType SQT = QT.split();
+ DeclarationFragments QualsFragments = getFragmentsForQualifiers(SQT.Quals),
+ TypeFragments =
+ getFragmentsForType(SQT.Ty, Context, After);
+ if (QualsFragments.getFragments().empty())
+ return TypeFragments;
+
+ // Use east qualifier for pointer types
+ // For example:
+ // ```
+ // int * const
+ // ^---- ^----
+ // type qualifier
+ // ^-----------------
+ // const pointer to int
+ // ```
+ // should not be reconstructed as
+ // ```
+ // const int *
+ // ^---- ^--
+ // qualifier type
+ // ^---------------- ^
+ // pointer to const int
+ // ```
+ if (SQT.Ty->isAnyPointerType())
+ return TypeFragments.appendSpace().append(std::move(QualsFragments));
+
+ return QualsFragments.appendSpace().append(std::move(TypeFragments));
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
+ DeclarationFragments Fragments;
+ StorageClass SC = Var->getStorageClass();
+ if (SC != SC_None)
+ Fragments
+ .append(VarDecl::getStorageClassSpecifierString(SC),
+ DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+ QualType T =
+ Var->getTypeSourceInfo()
+ ? Var->getTypeSourceInfo()->getType()
+ : Var->getASTContext().getUnqualifiedObjCPointerType(Var->getType());
+
+ // Capture potential fragments that needs to be placed after the variable name
+ // ```
+ // int nums[5];
+ // char (*ptr_to_array)[6];
+ // ```
+ DeclarationFragments After;
+ return Fragments.append(getFragmentsForType(T, Var->getASTContext(), After))
+ .appendSpace()
+ .append(Var->getName(), DeclarationFragments::FragmentKind::Identifier)
+ .append(std::move(After));
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForParam(const ParmVarDecl *Param) {
+ DeclarationFragments Fragments, After;
+
+ QualType T = Param->getTypeSourceInfo()
+ ? Param->getTypeSourceInfo()->getType()
+ : Param->getASTContext().getUnqualifiedObjCPointerType(
+ Param->getType());
+
+ DeclarationFragments TypeFragments =
+ getFragmentsForType(T, Param->getASTContext(), After);
+
+ if (Param->isObjCMethodParameter())
+ Fragments.append("(", DeclarationFragments::FragmentKind::Text)
+ .append(std::move(TypeFragments))
+ .append(") ", DeclarationFragments::FragmentKind::Text);
+ else
+ Fragments.append(std::move(TypeFragments)).appendSpace();
+
+ return Fragments
+ .append(Param->getName(),
+ DeclarationFragments::FragmentKind::InternalParam)
+ .append(std::move(After));
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
+ DeclarationFragments Fragments;
+ // FIXME: Handle template specialization
+ switch (Func->getStorageClass()) {
+ case SC_None:
+ case SC_PrivateExtern:
+ break;
+ case SC_Extern:
+ Fragments.append("extern", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+ break;
+ case SC_Static:
+ Fragments.append("static", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+ break;
+ case SC_Auto:
+ case SC_Register:
+ llvm_unreachable("invalid for functions");
+ }
+ // FIXME: Handle C++ function specifiers: constexpr, consteval, explicit, etc.
+
+ // FIXME: Is `after` actually needed here?
+ DeclarationFragments After;
+ Fragments
+ .append(getFragmentsForType(Func->getReturnType(), Func->getASTContext(),
+ After))
+ .appendSpace()
+ .append(Func->getName(), DeclarationFragments::FragmentKind::Identifier)
+ .append(std::move(After));
+
+ Fragments.append("(", DeclarationFragments::FragmentKind::Text);
+ for (unsigned i = 0, end = Func->getNumParams(); i != end; ++i) {
+ if (i)
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ Fragments.append(getFragmentsForParam(Func->getParamDecl(i)));
+ }
+ Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+
+ // FIXME: Handle exception specifiers: throw, noexcept
+ return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForEnumConstant(
+ const EnumConstantDecl *EnumConstDecl) {
+ DeclarationFragments Fragments;
+ return Fragments.append(EnumConstDecl->getName(),
+ DeclarationFragments::FragmentKind::Identifier);
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
+ if (const auto *TypedefNameDecl = EnumDecl->getTypedefNameForAnonDecl())
+ return getFragmentsForTypedef(TypedefNameDecl);
+
+ DeclarationFragments Fragments, After;
+ Fragments.append("enum", DeclarationFragments::FragmentKind::Keyword);
+
+ if (!EnumDecl->getName().empty())
+ Fragments.appendSpace().append(
+ EnumDecl->getName(), DeclarationFragments::FragmentKind::Identifier);
+
+ QualType IntegerType = EnumDecl->getIntegerType();
+ if (!IntegerType.isNull())
+ Fragments.append(": ", DeclarationFragments::FragmentKind::Text)
+ .append(
+ getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After))
+ .append(std::move(After));
+
+ return Fragments;
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
+ DeclarationFragments After;
+ return getFragmentsForType(Field->getType(), Field->getASTContext(), After)
+ .appendSpace()
+ .append(Field->getName(), DeclarationFragments::FragmentKind::Identifier)
+ .append(std::move(After));
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
+ if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl())
+ return getFragmentsForTypedef(TypedefNameDecl);
+
+ DeclarationFragments Fragments;
+ Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
+
+ if (!Record->getName().empty())
+ Fragments.appendSpace().append(
+ Record->getName(), DeclarationFragments::FragmentKind::Identifier);
+ return Fragments;
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
+ const MacroDirective *MD) {
+ DeclarationFragments Fragments;
+ Fragments.append("#define", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+ Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier);
+
+ auto *MI = MD->getMacroInfo();
+
+ if (MI->isFunctionLike()) {
+ Fragments.append("(", DeclarationFragments::FragmentKind::Text);
+ unsigned numParameters = MI->getNumParams();
+ if (MI->isC99Varargs())
+ --numParameters;
+ for (unsigned i = 0; i < numParameters; ++i) {
+ if (i)
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ Fragments.append(MI->params()[i]->getName(),
+ DeclarationFragments::FragmentKind::InternalParam);
+ }
+ if (MI->isVariadic()) {
+ if (numParameters && MI->isC99Varargs())
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ Fragments.append("...", DeclarationFragments::FragmentKind::Text);
+ }
+ Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+ }
+ return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory(
+ const ObjCCategoryDecl *Category) {
+ DeclarationFragments Fragments;
+
+ SmallString<128> InterfaceUSR;
+ index::generateUSRForDecl(Category->getClassInterface(), InterfaceUSR);
+
+ Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace()
+ .append(Category->getClassInterface()->getName(),
+ DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR)
+ .append(" (", DeclarationFragments::FragmentKind::Text)
+ .append(Category->getName(),
+ DeclarationFragments::FragmentKind::Identifier)
+ .append(")", DeclarationFragments::FragmentKind::Text);
+
+ return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCInterface(
+ const ObjCInterfaceDecl *Interface) {
+ DeclarationFragments Fragments;
+ // Build the base of the Objective-C interface declaration.
+ Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace()
+ .append(Interface->getName(),
+ DeclarationFragments::FragmentKind::Identifier);
+
+ // Build the inheritance part of the declaration.
+ if (const ObjCInterfaceDecl *SuperClass = Interface->getSuperClass()) {
+ SmallString<128> SuperUSR;
+ index::generateUSRForDecl(SuperClass, SuperUSR);
+ Fragments.append(" : ", DeclarationFragments::FragmentKind::Text)
+ .append(SuperClass->getName(),
+ DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR);
+ }
+
+ return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCMethod(
+ const ObjCMethodDecl *Method) {
+ DeclarationFragments Fragments, After;
+ // Build the instance/class method indicator.
+ if (Method->isClassMethod())
+ Fragments.append("+ ", DeclarationFragments::FragmentKind::Text);
+ else if (Method->isInstanceMethod())
+ Fragments.append("- ", DeclarationFragments::FragmentKind::Text);
+
+ // Build the return type.
+ Fragments.append("(", DeclarationFragments::FragmentKind::Text)
+ .append(getFragmentsForType(Method->getReturnType(),
+ Method->getASTContext(), After))
+ .append(std::move(After))
+ .append(")", DeclarationFragments::FragmentKind::Text);
+
+ // Build the selector part.
+ Selector Selector = Method->getSelector();
+ if (Selector.getNumArgs() == 0)
+ // For Objective-C methods that don't take arguments, the first (and only)
+ // slot of the selector is the method name.
+ Fragments.appendSpace().append(
+ Selector.getNameForSlot(0),
+ DeclarationFragments::FragmentKind::Identifier);
+
+ // For Objective-C methods that take arguments, build the selector slots.
+ for (unsigned i = 0, end = Method->param_size(); i != end; ++i) {
+ // Objective-C method selector parts are considered as identifiers instead
+ // of "external parameters" as in Swift. This is because Objective-C method
+ // symbols are referenced with the entire selector, instead of just the
+ // method name in Swift.
+ SmallString<32> ParamID(Selector.getNameForSlot(i));
+ ParamID.append(":");
+ Fragments.appendSpace().append(
+ ParamID, DeclarationFragments::FragmentKind::Identifier);
+
+ // Build the internal parameter.
+ const ParmVarDecl *Param = Method->getParamDecl(i);
+ Fragments.append(getFragmentsForParam(Param));
+ }
+
+ return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProperty(
+ const ObjCPropertyDecl *Property) {
+ DeclarationFragments Fragments, After;
+
+ // Build the Objective-C property keyword.
+ Fragments.append("@property", DeclarationFragments::FragmentKind::Keyword);
+
+ const auto Attributes = Property->getPropertyAttributes();
+ // Build the attributes if there is any associated with the property.
+ if (Attributes != ObjCPropertyAttribute::kind_noattr) {
+ // No leading comma for the first attribute.
+ bool First = true;
+ Fragments.append(" (", DeclarationFragments::FragmentKind::Text);
+ // Helper function to render the attribute.
+ auto RenderAttribute =
+ [&](ObjCPropertyAttribute::Kind Kind, StringRef Spelling,
+ StringRef Arg = "",
+ DeclarationFragments::FragmentKind ArgKind =
+ DeclarationFragments::FragmentKind::Identifier) {
+ // Check if the `Kind` attribute is set for this property.
+ if ((Attributes & Kind) && !Spelling.empty()) {
+ // Add a leading comma if this is not the first attribute rendered.
+ if (!First)
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ // Render the spelling of this attribute `Kind` as a keyword.
+ Fragments.append(Spelling,
+ DeclarationFragments::FragmentKind::Keyword);
+ // If this attribute takes in arguments (e.g. `getter=getterName`),
+ // render the arguments.
+ if (!Arg.empty())
+ Fragments.append("=", DeclarationFragments::FragmentKind::Text)
+ .append(Arg, ArgKind);
+ First = false;
+ }
+ };
+
+ // Go through all possible Objective-C property attributes and render set
+ // ones.
+ RenderAttribute(ObjCPropertyAttribute::kind_class, "class");
+ RenderAttribute(ObjCPropertyAttribute::kind_direct, "direct");
+ RenderAttribute(ObjCPropertyAttribute::kind_nonatomic, "nonatomic");
+ RenderAttribute(ObjCPropertyAttribute::kind_atomic, "atomic");
+ RenderAttribute(ObjCPropertyAttribute::kind_assign, "assign");
+ RenderAttribute(ObjCPropertyAttribute::kind_retain, "retain");
+ RenderAttribute(ObjCPropertyAttribute::kind_strong, "strong");
+ RenderAttribute(ObjCPropertyAttribute::kind_copy, "copy");
+ RenderAttribute(ObjCPropertyAttribute::kind_weak, "weak");
+ RenderAttribute(ObjCPropertyAttribute::kind_unsafe_unretained,
+ "unsafe_unretained");
+ RenderAttribute(ObjCPropertyAttribute::kind_readwrite, "readwrite");
+ RenderAttribute(ObjCPropertyAttribute::kind_readonly, "readonly");
+ RenderAttribute(ObjCPropertyAttribute::kind_getter, "getter",
+ Property->getGetterName().getAsString());
+ RenderAttribute(ObjCPropertyAttribute::kind_setter, "setter",
+ Property->getSetterName().getAsString());
+
+ // Render nullability attributes.
+ if (Attributes & ObjCPropertyAttribute::kind_nullability) {
+ QualType Type = Property->getType();
+ if (const auto Nullability =
+ AttributedType::stripOuterNullability(Type)) {
+ if (!First)
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+ if (*Nullability == NullabilityKind::Unspecified &&
+ (Attributes & ObjCPropertyAttribute::kind_null_resettable))
+ Fragments.append("null_resettable",
+ DeclarationFragments::FragmentKind::Keyword);
+ else
+ Fragments.append(
+ getNullabilitySpelling(*Nullability, /*isContextSensitive=*/true),
+ DeclarationFragments::FragmentKind::Keyword);
+ First = false;
+ }
+ }
+
+ Fragments.append(")", DeclarationFragments::FragmentKind::Text);
+ }
+
+ // Build the property type and name, and return the completed fragments.
+ return Fragments.appendSpace()
+ .append(getFragmentsForType(Property->getType(),
+ Property->getASTContext(), After))
+ .append(Property->getName(),
+ DeclarationFragments::FragmentKind::Identifier)
+ .append(std::move(After));
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(
+ const ObjCProtocolDecl *Protocol) {
+ DeclarationFragments Fragments;
+ // Build basic protocol declaration.
+ Fragments.append("@protocol", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace()
+ .append(Protocol->getName(),
+ DeclarationFragments::FragmentKind::Identifier);
+
+ // If this protocol conforms to other protocols, build the conformance list.
+ if (!Protocol->protocols().empty()) {
+ Fragments.append(" <", DeclarationFragments::FragmentKind::Text);
+ for (ObjCProtocolDecl::protocol_iterator It = Protocol->protocol_begin();
+ It != Protocol->protocol_end(); It++) {
+ // Add a leading comma if this is not the first protocol rendered.
+ if (It != Protocol->protocol_begin())
+ Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
+
+ SmallString<128> USR;
+ index::generateUSRForDecl(*It, USR);
+ Fragments.append((*It)->getName(),
+ DeclarationFragments::FragmentKind::TypeIdentifier, USR);
+ }
+ Fragments.append(">", DeclarationFragments::FragmentKind::Text);
+ }
+
+ return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
+ const TypedefNameDecl *Decl) {
+ DeclarationFragments Fragments, After;
+ Fragments.append("typedef", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace()
+ .append(getFragmentsForType(Decl->getUnderlyingType(),
+ Decl->getASTContext(), After))
+ .append(std::move(After))
+ .appendSpace()
+ .append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier);
+
+ return Fragments;
+}
+
+template <typename FunctionT>
+FunctionSignature
+DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
+ FunctionSignature Signature;
+
+ DeclarationFragments ReturnType, After;
+ ReturnType
+ .append(getFragmentsForType(Function->getReturnType(),
+ Function->getASTContext(), After))
+ .append(std::move(After));
+ Signature.setReturnType(ReturnType);
+
+ for (const auto *Param : Function->parameters())
+ Signature.addParameter(Param->getName(), getFragmentsForParam(Param));
+
+ return Signature;
+}
+
+// Instantiate template for FunctionDecl.
+template FunctionSignature
+DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *);
+
+// Instantiate template for ObjCMethodDecl.
+template FunctionSignature
+DeclarationFragmentsBuilder::getFunctionSignature(const ObjCMethodDecl *);
+
+// Subheading of a symbol defaults to its name.
+DeclarationFragments
+DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
+ DeclarationFragments Fragments;
+ if (!Decl->getName().empty())
+ Fragments.append(Decl->getName(),
+ DeclarationFragments::FragmentKind::Identifier);
+ return Fragments;
+}
+
+// Subheading of an Objective-C method is a `+` or `-` sign indicating whether
+// it's a class method or an instance method, followed by the selector name.
+DeclarationFragments
+DeclarationFragmentsBuilder::getSubHeading(const ObjCMethodDecl *Method) {
+ DeclarationFragments Fragments;
+ if (Method->isClassMethod())
+ Fragments.append("+ ", DeclarationFragments::FragmentKind::Text);
+ else if (Method->isInstanceMethod())
+ Fragments.append("- ", DeclarationFragments::FragmentKind::Text);
+
+ return Fragments.append(Method->getNameAsString(),
+ DeclarationFragments::FragmentKind::Identifier);
+}
+
+// Subheading of a symbol defaults to its name.
+DeclarationFragments
+DeclarationFragmentsBuilder::getSubHeadingForMacro(StringRef Name) {
+ DeclarationFragments Fragments;
+ Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier);
+ return Fragments;
+}
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
new file mode 100644
index 000000000000..bffa66c2d944
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp
@@ -0,0 +1,974 @@
+//===- ExtractAPI/ExtractAPIConsumer.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 ExtractAPIAction, and ASTVisitor/Consumer 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/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/FrontendActions.h"
+#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.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 <utility>
+
+using namespace clang;
+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) {
+ assert(CI.hasFileManager() &&
+ "CompilerInstance does not have a FileNamager!");
+
+ using namespace llvm::sys;
+ // Matches framework include patterns
+ const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
+
+ const auto &FS = CI.getVirtualFileSystem();
+
+ SmallString<128> FilePath(File.begin(), File.end());
+ FS.makeAbsolute(FilePath);
+ path::remove_dots(FilePath, true);
+ FilePath = path::convert_to_slash(FilePath);
+ File = FilePath;
+
+ // Checks whether `Dir` is a strict path prefix of `File`. If so returns
+ // the prefix length. Otherwise return 0.
+ auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
+ llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
+ FS.makeAbsolute(DirPath);
+ path::remove_dots(DirPath, true);
+ Dir = DirPath;
+ for (auto NI = path::begin(File), NE = path::end(File),
+ DI = path::begin(Dir), DE = path::end(Dir);
+ /*termination condition in loop*/; ++NI, ++DI) {
+ // '.' components in File are ignored.
+ while (NI != NE && *NI == ".")
+ ++NI;
+ if (NI == NE)
+ break;
+
+ // '.' components in Dir are ignored.
+ while (DI != DE && *DI == ".")
+ ++DI;
+
+ // Dir is a prefix of File, up to '.' components and choice of path
+ // separators.
+ if (DI == DE)
+ return NI - path::begin(File);
+
+ // Consider all path separators equal.
+ if (NI->size() == 1 && DI->size() == 1 &&
+ path::is_separator(NI->front()) && path::is_separator(DI->front()))
+ continue;
+
+ // Special case Apple .sdk folders since the search path is typically a
+ // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
+ // located in `iPhoneSimulator.sdk` (the real folder).
+ if (NI->endswith(".sdk") && DI->endswith(".sdk")) {
+ StringRef NBasename = path::stem(*NI);
+ StringRef DBasename = path::stem(*DI);
+ if (DBasename.startswith(NBasename))
+ continue;
+ }
+
+ if (*NI != *DI)
+ break;
+ }
+ return 0;
+ };
+
+ unsigned PrefixLength = 0;
+
+ // Go through the search paths and find the first one that is a prefix of
+ // the header.
+ for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
+ // Note whether the match is found in a quoted entry.
+ if (IsQuoted)
+ *IsQuoted = Entry.Group == frontend::Quoted;
+
+ if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
+ if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
+ // If this is a headermap entry, try to reverse lookup the full path
+ // for a spelled name before mapping.
+ StringRef SpelledFilename = HMap->reverseLookupFilename(File);
+ if (!SpelledFilename.empty())
+ return SpelledFilename.str();
+
+ // No matching mapping in this headermap, try next search entry.
+ continue;
+ }
+ }
+
+ // Entry is a directory search entry, try to check if it's a prefix of File.
+ PrefixLength = CheckDir(Entry.Path);
+ if (PrefixLength > 0) {
+ // The header is found in a framework path, construct the framework-style
+ // include name `<Framework/Header.h>`
+ if (Entry.IsFramework) {
+ SmallVector<StringRef, 4> Matches;
+ Rule.match(File, &Matches);
+ // Returned matches are always in stable order.
+ if (Matches.size() != 4)
+ return None;
+
+ return path::convert_to_slash(
+ (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
+ Matches[3])
+ .str());
+ }
+
+ // The header is found in a normal search path, strip the search path
+ // prefix to get an include name.
+ return path::convert_to_slash(File.drop_front(PrefixLength));
+ }
+ }
+
+ // Couldn't determine a include name, use full path instead.
+ return None;
+}
+
+struct LocationFileChecker {
+ bool isLocationInKnownFile(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();
+ auto FileLoc = SM.getFileLoc(Loc);
+ FileID FID = SM.getFileID(FileLoc);
+ if (FID.isInvalid())
+ return false;
+
+ const auto *File = SM.getFileEntryForID(FID);
+ if (!File)
+ return false;
+
+ if (KnownFileEntries.count(File))
+ return true;
+
+ if (ExternalFileEntries.count(File))
+ return false;
+
+ StringRef FileName = File->tryGetRealPathName().empty()
+ ? File->getName()
+ : File->tryGetRealPathName();
+
+ // Try to reduce the include name the same way we tried to include it.
+ bool IsQuoted = false;
+ if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
+ if (llvm::any_of(KnownFiles,
+ [&IsQuoted, &IncludeName](const auto &KnownFile) {
+ return KnownFile.first.equals(*IncludeName) &&
+ KnownFile.second == IsQuoted;
+ })) {
+ KnownFileEntries.insert(File);
+ return true;
+ }
+
+ // Record that the file was not found to avoid future reverse lookup for
+ // the same file.
+ ExternalFileEntries.insert(File);
+ return false;
+ }
+
+ LocationFileChecker(const CompilerInstance &CI,
+ SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
+ : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
+ for (const auto &KnownFile : KnownFiles)
+ if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
+ KnownFileEntries.insert(*FileEntry);
+ }
+
+private:
+ const CompilerInstance &CI;
+ SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
+ llvm::DenseSet<const FileEntry *> KnownFileEntries;
+ 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:
+ 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,
+ std::unique_ptr<LocationFileChecker> LCF, APISet &API)
+ : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ // Use ExtractAPIVisitor to traverse symbol declarations in the context.
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+ }
+
+private:
+ ExtractAPIVisitor Visitor;
+ std::unique_ptr<LocationFileChecker> LCF;
+};
+
+class MacroCallback : public PPCallbacks {
+public:
+ MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
+ Preprocessor &PP)
+ : SM(SM), LCF(LCF), API(API), PP(PP) {}
+
+ void MacroDefined(const Token &MacroNameToken,
+ const MacroDirective *MD) override {
+ auto *MacroInfo = MD->getMacroInfo();
+
+ if (MacroInfo->isBuiltinMacro())
+ return;
+
+ auto SourceLoc = MacroNameToken.getLocation();
+ if (SM.isWrittenInBuiltinFile(SourceLoc) ||
+ SM.isWrittenInCommandLineFile(SourceLoc))
+ return;
+
+ PendingMacros.emplace_back(MacroNameToken, MD);
+ }
+
+ // If a macro gets undefined at some point during preprocessing of the inputs
+ // it means that it isn't an exposed API and we should therefore not add a
+ // macro definition for it.
+ void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
+ const MacroDirective *Undef) override {
+ // If this macro wasn't previously defined we don't need to do anything
+ // here.
+ if (!Undef)
+ return;
+
+ llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
+ return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
+ /*Syntactically*/ false);
+ });
+ }
+
+ void EndOfMainFile() override {
+ for (auto &PM : PendingMacros) {
+ // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
+ // file so check for it here.
+ if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
+ continue;
+
+ if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
+ continue;
+
+ StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
+ PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
+ StringRef USR =
+ API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
+
+ API.addMacroDefinition(
+ Name, USR, Loc,
+ DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
+ DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
+ }
+
+ PendingMacros.clear();
+ }
+
+private:
+ struct PendingMacro {
+ Token MacroNameToken;
+ const MacroDirective *MD;
+
+ PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
+ : MacroNameToken(MacroNameToken), MD(MD) {}
+ };
+
+ const SourceManager &SM;
+ LocationFileChecker &LCF;
+ APISet &API;
+ Preprocessor &PP;
+ llvm::SmallVector<PendingMacro> PendingMacros;
+};
+
+} // namespace
+
+std::unique_ptr<ASTConsumer>
+ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
+ OS = CreateOutputFile(CI, InFile);
+ if (!OS)
+ return nullptr;
+
+ 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());
+
+ auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
+
+ CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
+ CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
+
+ return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
+ std::move(LCF), *API);
+}
+
+bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
+ auto &Inputs = CI.getFrontendOpts().Inputs;
+ if (Inputs.empty())
+ return true;
+
+ if (!CI.hasFileManager())
+ if (!CI.createFileManager())
+ return false;
+
+ auto Kind = Inputs[0].getKind();
+
+ // Convert the header file inputs into a single input buffer.
+ SmallString<256> HeaderContents;
+ bool IsQuoted = false;
+ for (const FrontendInputFile &FIF : Inputs) {
+ if (Kind.isObjectiveC())
+ HeaderContents += "#import";
+ else
+ HeaderContents += "#include";
+
+ StringRef FilePath = FIF.getFile();
+ if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
+ if (IsQuoted)
+ HeaderContents += " \"";
+ else
+ HeaderContents += " <";
+
+ HeaderContents += *RelativeName;
+
+ if (IsQuoted)
+ HeaderContents += "\"\n";
+ else
+ HeaderContents += ">\n";
+ KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
+ IsQuoted);
+ } else {
+ HeaderContents += " \"";
+ HeaderContents += FilePath;
+ HeaderContents += "\"\n";
+ KnownInputFiles.emplace_back(FilePath, true);
+ }
+ }
+
+ if (CI.getHeaderSearchOpts().Verbose)
+ CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
+ << HeaderContents << "\n";
+
+ Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
+ getInputBufferName());
+
+ // Set that buffer up as our "real" input in the CompilerInstance.
+ Inputs.clear();
+ Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
+
+ return true;
+}
+
+void ExtractAPIAction::EndSourceFileAction() {
+ if (!OS)
+ return;
+
+ // 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);
+ SGSerializer.serialize(*OS);
+ OS.reset();
+}
+
+std::unique_ptr<raw_pwrite_stream>
+ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
+ std::unique_ptr<raw_pwrite_stream> OS =
+ CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
+ /*RemoveFileOnSignal=*/false);
+ if (!OS)
+ return nullptr;
+ return OS;
+}
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SerializerBase.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SerializerBase.cpp
new file mode 100644
index 000000000000..71fd25b2b2ab
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SerializerBase.cpp
@@ -0,0 +1,19 @@
+//===- ExtractAPI/Serialization/SerializerBase.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 APISerializer interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ExtractAPI/Serialization/SerializerBase.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::extractapi;
+
+void APISerializer::serialize(llvm::raw_ostream &os) {}
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
new file mode 100644
index 000000000000..c4797cea333f
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -0,0 +1,708 @@
+//===- ExtractAPI/Serialization/SymbolGraphSerializer.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 SymbolGraphSerializer.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
+#include "clang/Basic/Version.h"
+#include "clang/ExtractAPI/API.h"
+#include "clang/ExtractAPI/DeclarationFragments.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/VersionTuple.h"
+#include <type_traits>
+
+using namespace clang;
+using namespace clang::extractapi;
+using namespace llvm;
+using namespace llvm::json;
+
+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) {
+ if (Obj)
+ Paren[Key] = std::move(Obj.getValue());
+}
+
+/// 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) {
+ if (Array)
+ Paren[Key] = std::move(Array.getValue());
+}
+
+/// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version
+/// format.
+///
+/// A semantic version object contains three numeric fields, representing the
+/// \c major, \c minor, and \c patch parts of the version tuple.
+/// For example version tuple 1.0.3 is serialized as:
+/// \code
+/// {
+/// "major" : 1,
+/// "minor" : 0,
+/// "patch" : 3
+/// }
+/// \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) {
+ if (V.empty())
+ return None;
+
+ Object Version;
+ Version["major"] = V.getMajor();
+ Version["minor"] = V.getMinor().value_or(0);
+ Version["patch"] = V.getSubminor().value_or(0);
+ return Version;
+}
+
+/// Serialize the OS information in the Symbol Graph platform property.
+///
+/// The OS information in Symbol Graph contains the \c name of the OS, and an
+/// optional \c minimumVersion semantic version field.
+Object serializeOperatingSystem(const Triple &T) {
+ Object OS;
+ OS["name"] = T.getOSTypeName(T.getOS());
+ serializeObject(OS, "minimumVersion",
+ serializeSemanticVersion(T.getMinimumSupportedOSVersion()));
+ return OS;
+}
+
+/// Serialize the platform information in the Symbol Graph module section.
+///
+/// The platform object describes a target platform triple in corresponding
+/// three fields: \c architecture, \c vendor, and \c operatingSystem.
+Object serializePlatform(const Triple &T) {
+ Object Platform;
+ Platform["architecture"] = T.getArchName();
+ Platform["vendor"] = T.getVendorName();
+ Platform["operatingSystem"] = serializeOperatingSystem(T);
+ return Platform;
+}
+
+/// Serialize a source position.
+Object serializeSourcePosition(const PresumedLoc &Loc) {
+ assert(Loc.isValid() && "invalid source position");
+
+ Object SourcePosition;
+ SourcePosition["line"] = Loc.getLine();
+ SourcePosition["character"] = Loc.getColumn();
+
+ return SourcePosition;
+}
+
+/// Serialize a source location in file.
+///
+/// \param Loc The presumed location to serialize.
+/// \param IncludeFileURI If true, include the file path of \p Loc as a URI.
+/// Defaults to false.
+Object serializeSourceLocation(const PresumedLoc &Loc,
+ bool IncludeFileURI = false) {
+ Object SourceLocation;
+ serializeObject(SourceLocation, "position", serializeSourcePosition(Loc));
+
+ if (IncludeFileURI) {
+ std::string FileURI = "file://";
+ // Normalize file path to use forward slashes for the URI.
+ FileURI += sys::path::convert_to_slash(Loc.getFilename());
+ SourceLocation["uri"] = FileURI;
+ }
+
+ return SourceLocation;
+}
+
+/// Serialize a source range with begin and end locations.
+Object serializeSourceRange(const PresumedLoc &BeginLoc,
+ const PresumedLoc &EndLoc) {
+ Object SourceRange;
+ serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc));
+ serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc));
+ return SourceRange;
+}
+
+/// 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)).
+///
+/// \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;
+}
+
+/// Get the language name string for interface language references.
+StringRef getLanguageName(Language Lang) {
+ switch (Lang) {
+ case Language::C:
+ return "c";
+ case Language::ObjC:
+ return "objective-c";
+
+ // Unsupported language currently
+ case Language::CXX:
+ case Language::ObjCXX:
+ case Language::OpenCL:
+ case Language::OpenCLCXX:
+ case Language::CUDA:
+ case Language::RenderScript:
+ case Language::HIP:
+ case Language::HLSL:
+
+ // Languages that the frontend cannot parse and compile
+ case Language::Unknown:
+ case Language::Asm:
+ case Language::LLVM_IR:
+ llvm_unreachable("Unsupported language kind");
+ }
+
+ llvm_unreachable("Unhandled language kind");
+}
+
+/// Serialize the identifier object as specified by the Symbol Graph format.
+///
+/// The identifier property of a symbol contains the USR for precise and unique
+/// references, and the interface language name.
+Object serializeIdentifier(const APIRecord &Record, Language Lang) {
+ Object Identifier;
+ Identifier["precise"] = Record.USR;
+ Identifier["interfaceLanguage"] = getLanguageName(Lang);
+
+ return Identifier;
+}
+
+/// Serialize the documentation comments attached to a symbol, as specified by
+/// the Symbol Graph format.
+///
+/// The Symbol Graph \c docComment object contains an array of lines. Each line
+/// represents one line of striped documentation comment, with source range
+/// information.
+/// e.g.
+/// \code
+/// /// This is a documentation comment
+/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line.
+/// /// with multiple lines.
+/// ^~~~~~~~~~~~~~~~~~~~~~~' 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) {
+ if (Comment.empty())
+ return None;
+
+ Object DocComment;
+ Array LinesArray;
+ for (const auto &CommentLine : Comment) {
+ Object Line;
+ Line["text"] = CommentLine.Text;
+ serializeObject(Line, "range",
+ serializeSourceRange(CommentLine.Begin, CommentLine.End));
+ LinesArray.emplace_back(std::move(Line));
+ }
+ serializeArray(DocComment, "lines", LinesArray);
+
+ return DocComment;
+}
+
+/// Serialize the declaration fragments of a symbol.
+///
+/// The Symbol Graph declaration fragments is an array of tagged important
+/// parts of a symbol's declaration. The fragments sequence can be joined to
+/// form spans of declaration text, with attached information useful for
+/// purposes like syntax-highlighting etc. For example:
+/// \code
+/// const int pi; -> "declarationFragments" : [
+/// {
+/// "kind" : "keyword",
+/// "spelling" : "const"
+/// },
+/// {
+/// "kind" : "text",
+/// "spelling" : " "
+/// },
+/// {
+/// "kind" : "typeIdentifier",
+/// "preciseIdentifier" : "c:I",
+/// "spelling" : "int"
+/// },
+/// {
+/// "kind" : "text",
+/// "spelling" : " "
+/// },
+/// {
+/// "kind" : "identifier",
+/// "spelling" : "pi"
+/// }
+/// ]
+/// \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) {
+ if (DF.getFragments().empty())
+ return None;
+
+ Array Fragments;
+ for (const auto &F : DF.getFragments()) {
+ Object Fragment;
+ Fragment["spelling"] = F.Spelling;
+ Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind);
+ if (!F.PreciseIdentifier.empty())
+ Fragment["preciseIdentifier"] = F.PreciseIdentifier;
+ Fragments.emplace_back(std::move(Fragment));
+ }
+
+ return Fragments;
+}
+
+/// Serialize the \c names field of a symbol as specified by the Symbol Graph
+/// format.
+///
+/// The Symbol Graph names field contains multiple representations of a symbol
+/// that can be used for different applications:
+/// - \c title : The simple declared name of the symbol;
+/// - \c subHeading : An array of declaration fragments that provides tags,
+/// and potentially more tokens (for example the \c +/- symbol for
+/// Objective-C methods). Can be used as sub-headings for documentation.
+Object serializeNames(const APIRecord &Record) {
+ Object Names;
+ Names["title"] = Record.Name;
+ serializeArray(Names, "subHeading",
+ serializeDeclarationFragments(Record.SubHeading));
+ DeclarationFragments NavigatorFragments;
+ NavigatorFragments.append(Record.Name,
+ DeclarationFragments::FragmentKind::Identifier,
+ /*PreciseIdentifier*/ "");
+ serializeArray(Names, "navigator",
+ serializeDeclarationFragments(NavigatorFragments));
+
+ 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) {
+ auto AddLangPrefix = [&Lang](StringRef S) -> std::string {
+ return (getLanguageName(Lang) + "." + S).str();
+ };
+
+ Object Kind;
+ switch (Record.getKind()) {
+ case APIRecord::RK_GlobalFunction:
+ Kind["identifier"] = AddLangPrefix("func");
+ Kind["displayName"] = "Function";
+ break;
+ case APIRecord::RK_GlobalVariable:
+ Kind["identifier"] = AddLangPrefix("var");
+ Kind["displayName"] = "Global Variable";
+ break;
+ case APIRecord::RK_EnumConstant:
+ Kind["identifier"] = AddLangPrefix("enum.case");
+ Kind["displayName"] = "Enumeration Case";
+ break;
+ case APIRecord::RK_Enum:
+ Kind["identifier"] = AddLangPrefix("enum");
+ Kind["displayName"] = "Enumeration";
+ break;
+ case APIRecord::RK_StructField:
+ Kind["identifier"] = AddLangPrefix("property");
+ Kind["displayName"] = "Instance Property";
+ break;
+ case APIRecord::RK_Struct:
+ Kind["identifier"] = AddLangPrefix("struct");
+ Kind["displayName"] = "Structure";
+ break;
+ case APIRecord::RK_ObjCIvar:
+ 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";
+ }
+ break;
+ case APIRecord::RK_ObjCProperty:
+ Kind["identifier"] = AddLangPrefix("property");
+ Kind["displayName"] = "Instance Property";
+ break;
+ case APIRecord::RK_ObjCInterface:
+ Kind["identifier"] = AddLangPrefix("class");
+ Kind["displayName"] = "Class";
+ break;
+ case APIRecord::RK_ObjCCategory:
+ // We don't serialize out standalone Objective-C category symbols yet.
+ llvm_unreachable("Serializing standalone Objective-C category symbols is "
+ "not supported.");
+ break;
+ case APIRecord::RK_ObjCProtocol:
+ Kind["identifier"] = AddLangPrefix("protocol");
+ Kind["displayName"] = "Protocol";
+ break;
+ case APIRecord::RK_MacroDefinition:
+ Kind["identifier"] = AddLangPrefix("macro");
+ Kind["displayName"] = "Macro";
+ break;
+ case APIRecord::RK_Typedef:
+ Kind["identifier"] = AddLangPrefix("typealias");
+ Kind["displayName"] = "Type Alias";
+ break;
+ }
+
+ return Kind;
+}
+
+template <typename RecordTy>
+Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
+ std::true_type) {
+ const auto &FS = Record.Signature;
+ if (FS.empty())
+ return None;
+
+ Object Signature;
+ serializeArray(Signature, "returns",
+ serializeDeclarationFragments(FS.getReturnType()));
+
+ Array Parameters;
+ for (const auto &P : FS.getParameters()) {
+ Object Parameter;
+ Parameter["name"] = P.Name;
+ serializeArray(Parameter, "declarationFragments",
+ serializeDeclarationFragments(P.Fragments));
+ Parameters.emplace_back(std::move(Parameter));
+ }
+
+ if (!Parameters.empty())
+ Signature["parameters"] = std::move(Parameters);
+
+ return Signature;
+}
+
+template <typename RecordTy>
+Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
+ std::false_type) {
+ return None;
+}
+
+/// Serialize the function signature field, as specified by the
+/// Symbol Graph format.
+///
+/// The Symbol Graph function signature property contains two arrays.
+/// - The \c returns array is the declaration fragments of the return type;
+/// - 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
+/// formatted function signature.
+template <typename RecordTy>
+void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
+ serializeObject(Paren, "functionSignature",
+ serializeFunctionSignatureMixinImpl(
+ Record, has_function_signature<RecordTy>()));
+}
+
+} // namespace
+
+void SymbolGraphSerializer::anchor() {}
+
+/// Defines the format version emitted by SymbolGraphSerializer.
+const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3};
+
+Object SymbolGraphSerializer::serializeMetadata() const {
+ Object Metadata;
+ serializeObject(Metadata, "formatVersion",
+ serializeSemanticVersion(FormatVersion));
+ Metadata["generator"] = clang::getClangFullVersion();
+ return Metadata;
+}
+
+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;
+ serializeObject(Module, "platform", serializePlatform(API.getTarget()));
+ return Module;
+}
+
+bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const {
+ // Skip unconditionally unavailable symbols
+ if (Record.Availability.isUnconditionallyUnavailable())
+ return true;
+
+ // Filter out symbols prefixed with an underscored as they are understood to
+ // be symbols clients should not use.
+ if (Record.Name.startswith("_"))
+ return true;
+
+ return false;
+}
+
+template <typename RecordTy>
+Optional<Object>
+SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
+ if (shouldSkip(Record))
+ return None;
+
+ Object Obj;
+ serializeObject(Obj, "identifier",
+ serializeIdentifier(Record, API.getLanguage()));
+ serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLanguage()));
+ serializeObject(Obj, "names", serializeNames(Record));
+ serializeObject(
+ Obj, "location",
+ serializeSourceLocation(Record.Location, /*IncludeFileURI=*/true));
+ serializeObject(Obj, "availbility",
+ serializeAvailability(Record.Availability));
+ 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));
+
+ serializeFunctionSignatureMixin(Obj, Record);
+
+ return Obj;
+}
+
+template <typename MemberTy>
+void SymbolGraphSerializer::serializeMembers(
+ const APIRecord &Record,
+ const SmallVector<std::unique_ptr<MemberTy>> &Members) {
+ for (const auto &Member : Members) {
+ auto MemberPathComponentGuard = makePathComponentGuard(Member->Name);
+ auto MemberRecord = serializeAPIRecord(*Member);
+ if (!MemberRecord)
+ continue;
+
+ Symbols.emplace_back(std::move(*MemberRecord));
+ serializeRelationship(RelationshipKind::MemberOf, *Member, Record);
+ }
+}
+
+StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
+ switch (Kind) {
+ case RelationshipKind::MemberOf:
+ return "memberOf";
+ case RelationshipKind::InheritsFrom:
+ return "inheritsFrom";
+ case RelationshipKind::ConformsTo:
+ return "conformsTo";
+ }
+ llvm_unreachable("Unhandled relationship kind");
+}
+
+void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
+ SymbolReference Source,
+ SymbolReference Target) {
+ Object Relationship;
+ Relationship["source"] = Source.USR;
+ Relationship["target"] = Target.USR;
+ Relationship["kind"] = getRelationshipString(Kind);
+
+ Relationships.emplace_back(std::move(Relationship));
+}
+
+void SymbolGraphSerializer::serializeGlobalFunctionRecord(
+ const GlobalFunctionRecord &Record) {
+ auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
+
+ auto Obj = serializeAPIRecord(Record);
+ if (!Obj)
+ return;
+
+ Symbols.emplace_back(std::move(*Obj));
+}
+
+void SymbolGraphSerializer::serializeGlobalVariableRecord(
+ const GlobalVariableRecord &Record) {
+ auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
+
+ auto Obj = serializeAPIRecord(Record);
+ if (!Obj)
+ return;
+
+ Symbols.emplace_back(std::move(*Obj));
+}
+
+void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
+ auto EnumPathComponentGuard = makePathComponentGuard(Record.Name);
+ auto Enum = serializeAPIRecord(Record);
+ if (!Enum)
+ return;
+
+ Symbols.emplace_back(std::move(*Enum));
+ serializeMembers(Record, Record.Constants);
+}
+
+void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
+ auto StructPathComponentGuard = makePathComponentGuard(Record.Name);
+ auto Struct = serializeAPIRecord(Record);
+ if (!Struct)
+ return;
+
+ Symbols.emplace_back(std::move(*Struct));
+ serializeMembers(Record, Record.Fields);
+}
+
+void SymbolGraphSerializer::serializeObjCContainerRecord(
+ const ObjCContainerRecord &Record) {
+ auto ObjCContainerPathComponentGuard = makePathComponentGuard(Record.Name);
+ auto ObjCContainer = serializeAPIRecord(Record);
+ if (!ObjCContainer)
+ return;
+
+ Symbols.emplace_back(std::move(*ObjCContainer));
+
+ serializeMembers(Record, Record.Ivars);
+ serializeMembers(Record, Record.Methods);
+ serializeMembers(Record, Record.Properties);
+
+ for (const auto &Protocol : Record.Protocols)
+ // Record that Record conforms to Protocol.
+ serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
+
+ if (auto *ObjCInterface = dyn_cast<ObjCInterfaceRecord>(&Record)) {
+ if (!ObjCInterface->SuperClass.empty())
+ // If Record is an Objective-C interface record and it has a super class,
+ // record that Record is inherited from SuperClass.
+ serializeRelationship(RelationshipKind::InheritsFrom, Record,
+ ObjCInterface->SuperClass);
+
+ // Members of categories extending an interface are serialized as members of
+ // the interface.
+ for (const auto *Category : ObjCInterface->Categories) {
+ serializeMembers(Record, Category->Ivars);
+ serializeMembers(Record, Category->Methods);
+ serializeMembers(Record, Category->Properties);
+
+ // Surface the protocols of the the category to the interface.
+ for (const auto &Protocol : Category->Protocols)
+ serializeRelationship(RelationshipKind::ConformsTo, Record, Protocol);
+ }
+ }
+}
+
+void SymbolGraphSerializer::serializeMacroDefinitionRecord(
+ const MacroDefinitionRecord &Record) {
+ auto MacroPathComponentGuard = makePathComponentGuard(Record.Name);
+ auto Macro = serializeAPIRecord(Record);
+
+ if (!Macro)
+ return;
+
+ Symbols.emplace_back(std::move(*Macro));
+}
+
+void SymbolGraphSerializer::serializeTypedefRecord(
+ const TypedefRecord &Record) {
+ // Typedefs of anonymous types have their entries unified with the underlying
+ // type.
+ bool ShouldDrop = Record.UnderlyingType.Name.empty();
+ // enums declared with `NS_OPTION` have a named enum and a named typedef, with
+ // the same name
+ ShouldDrop |= (Record.UnderlyingType.Name == Record.Name);
+ if (ShouldDrop)
+ return;
+
+ auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name);
+ auto Typedef = serializeAPIRecord(Record);
+ if (!Typedef)
+ return;
+
+ (*Typedef)["type"] = Record.UnderlyingType.USR;
+
+ 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);
+
+ for (const auto &GlobalFunction : API.getGlobalFunctions())
+ serializeGlobalFunctionRecord(*GlobalFunction.second);
+
+ // Serialize enum records in the API set.
+ for (const auto &Enum : API.getEnums())
+ serializeEnumRecord(*Enum.second);
+
+ // Serialize struct records in the API set.
+ for (const auto &Struct : API.getStructs())
+ serializeStructRecord(*Struct.second);
+
+ // Serialize Objective-C interface records in the API set.
+ for (const auto &ObjCInterface : API.getObjCInterfaces())
+ serializeObjCContainerRecord(*ObjCInterface.second);
+
+ // Serialize Objective-C protocol records in the API set.
+ for (const auto &ObjCProtocol : API.getObjCProtocols())
+ serializeObjCContainerRecord(*ObjCProtocol.second);
+
+ for (const auto &Macro : API.getMacros())
+ serializeMacroDefinitionRecord(*Macro.second);
+
+ for (const auto &Typedef : API.getTypedefs())
+ serializeTypedefRecord(*Typedef.second);
+
+ Root["symbols"] = std::move(Symbols);
+ Root["relationships"] = std::move(Relationships);
+
+ return Root;
+}
+
+void SymbolGraphSerializer::serialize(raw_ostream &os) {
+ Object root = serialize();
+ if (Options.Compact)
+ os << formatv("{0}", Value(std::move(root))) << "\n";
+ else
+ os << formatv("{0:2}", Value(std::move(root))) << "\n";
+}
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
new file mode 100644
index 000000000000..9c165e693e0e
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp
@@ -0,0 +1,79 @@
+//===- ExtractAPI/TypedefUnderlyingTypeResolver.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 UnderlyingTypeResolver.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TypedefUnderlyingTypeResolver.h"
+#include "clang/Index/USRGeneration.h"
+
+using namespace clang;
+using namespace extractapi;
+
+namespace {
+
+const NamedDecl *getUnderlyingTypeDecl(QualType Type) {
+ const NamedDecl *TypeDecl = nullptr;
+
+ const TypedefType *TypedefTy = Type->getAs<TypedefType>();
+ if (TypedefTy)
+ TypeDecl = TypedefTy->getDecl();
+ if (const TagType *TagTy = Type->getAs<TagType>()) {
+ TypeDecl = TagTy->getDecl();
+ } else if (const ObjCInterfaceType *ObjCITy =
+ Type->getAs<ObjCInterfaceType>()) {
+ TypeDecl = ObjCITy->getDecl();
+ }
+
+ if (TypeDecl && TypedefTy) {
+ // if this is a typedef to another typedef, use the typedef's decl for the
+ // USR - this will actually be in the output, unlike a typedef to an
+ // anonymous decl
+ const TypedefNameDecl *TypedefDecl = TypedefTy->getDecl();
+ if (TypedefDecl->getUnderlyingType()->isTypedefNameType())
+ TypeDecl = TypedefDecl;
+ }
+
+ return TypeDecl;
+}
+
+} // namespace
+
+SymbolReference
+TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type,
+ APISet &API) const {
+ std::string TypeName = Type.getAsString();
+ SmallString<128> TypeUSR;
+ const NamedDecl *TypeDecl = getUnderlyingTypeDecl(Type);
+ const TypedefType *TypedefTy = Type->getAs<TypedefType>();
+
+ if (TypeDecl) {
+ if (!TypedefTy)
+ TypeName = TypeDecl->getName().str();
+
+ clang::index::generateUSRForDecl(TypeDecl, TypeUSR);
+ } else {
+ clang::index::generateUSRForType(Type, Context, TypeUSR);
+ }
+
+ return {API.copyString(TypeName), API.copyString(TypeUSR)};
+}
+
+std::string TypedefUnderlyingTypeResolver::getUSRForType(QualType Type) const {
+ SmallString<128> TypeUSR;
+ const NamedDecl *TypeDecl = getUnderlyingTypeDecl(Type);
+
+ if (TypeDecl)
+ clang::index::generateUSRForDecl(TypeDecl, TypeUSR);
+ else
+ clang::index::generateUSRForType(Type, Context, TypeUSR);
+
+ return std::string(TypeUSR);
+}
diff --git a/contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h b/contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
new file mode 100644
index 000000000000..0096ff235914
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h
@@ -0,0 +1,46 @@
+//===- ExtractAPI/TypedefUnderlyingTypeResolver.h ---------------*- 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 defines the UnderlyingTypeResolver which is a helper type for
+/// resolving the undelrying type for a given QualType and exposing that
+/// information in various forms.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_UNDERLYING_TYPE_RESOLVER_H
+#define LLVM_CLANG_UNDERLYING_TYPE_RESOLVER_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ExtractAPI/API.h"
+
+#include <string>
+
+namespace clang {
+namespace extractapi {
+
+struct TypedefUnderlyingTypeResolver {
+
+ /// Get a SymbolReference for the given type.
+ SymbolReference getSymbolReferenceForType(QualType Type, APISet &API) const;
+
+ /// Get a USR for the given type.
+ std::string getUSRForType(QualType Type) const;
+
+ explicit TypedefUnderlyingTypeResolver(ASTContext &Context)
+ : Context(Context) {}
+
+private:
+ ASTContext &Context;
+};
+
+} // namespace extractapi
+} // namespace clang
+
+#endif // LLVM_CLANG_UNDERLYING_TYPE_RESOLVER_H