diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/ExtractAPI')
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 |
