| //===- 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(DenseMap<StringRef, APIRecord *> &USRLookupTable, |
| 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)...); |
| |
| auto *Record = Result.first->second.get(); |
| USRLookupTable.insert({USR, Record}); |
| return Record; |
| } |
| |
| } // namespace |
| |
| GlobalVariableRecord * |
| APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, LinkageInfo Linkage, |
| const DocComment &Comment, DeclarationFragments Fragments, |
| DeclarationFragments SubHeading, bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, GlobalVariables, USR, Name, Loc, |
| std::move(Availabilities), Linkage, Comment, |
| Fragments, SubHeading, IsFromSystemHeader); |
| } |
| |
| GlobalFunctionRecord *APISet::addGlobalFunction( |
| StringRef Name, StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, LinkageInfo Linkage, |
| const DocComment &Comment, DeclarationFragments Fragments, |
| DeclarationFragments SubHeading, FunctionSignature Signature, |
| bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, GlobalFunctions, USR, Name, Loc, |
| std::move(Availabilities), Linkage, Comment, |
| Fragments, SubHeading, Signature, |
| IsFromSystemHeader); |
| } |
| |
| EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name, |
| StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, |
| const DocComment &Comment, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| bool IsFromSystemHeader) { |
| auto Record = std::make_unique<EnumConstantRecord>( |
| USR, Name, Loc, std::move(Availabilities), Comment, Declaration, |
| SubHeading, IsFromSystemHeader); |
| Record->ParentInformation = APIRecord::HierarchyInformation( |
| Enum->USR, Enum->Name, Enum->getKind(), Enum); |
| USRBasedLookupTable.insert({USR, Record.get()}); |
| return Enum->Constants.emplace_back(std::move(Record)).get(); |
| } |
| |
| EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, |
| const DocComment &Comment, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, Enums, USR, Name, Loc, |
| std::move(Availabilities), Comment, Declaration, |
| SubHeading, IsFromSystemHeader); |
| } |
| |
| StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name, |
| StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, |
| const DocComment &Comment, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| bool IsFromSystemHeader) { |
| auto Record = std::make_unique<StructFieldRecord>( |
| USR, Name, Loc, std::move(Availabilities), Comment, Declaration, |
| SubHeading, IsFromSystemHeader); |
| Record->ParentInformation = APIRecord::HierarchyInformation( |
| Struct->USR, Struct->Name, Struct->getKind(), Struct); |
| USRBasedLookupTable.insert({USR, Record.get()}); |
| return Struct->Fields.emplace_back(std::move(Record)).get(); |
| } |
| |
| StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, |
| const DocComment &Comment, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, Structs, USR, Name, Loc, |
| std::move(Availabilities), Comment, Declaration, |
| SubHeading, IsFromSystemHeader); |
| } |
| |
| ObjCCategoryRecord *APISet::addObjCCategory( |
| StringRef Name, StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, const DocComment &Comment, |
| DeclarationFragments Declaration, DeclarationFragments SubHeading, |
| SymbolReference Interface, bool IsFromSystemHeader) { |
| // Create the category record. |
| auto *Record = |
| addTopLevelRecord(USRBasedLookupTable, ObjCCategories, USR, Name, Loc, |
| std::move(Availabilities), Comment, Declaration, |
| SubHeading, Interface, IsFromSystemHeader); |
| |
| // If this category is extending a known interface, associate it with the |
| // ObjCInterfaceRecord. |
| 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, |
| AvailabilitySet Availabilities, LinkageInfo Linkage, |
| const DocComment &Comment, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| SymbolReference SuperClass, bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, ObjCInterfaces, USR, Name, Loc, |
| std::move(Availabilities), Linkage, Comment, |
| Declaration, SubHeading, SuperClass, |
| IsFromSystemHeader); |
| } |
| |
| ObjCMethodRecord *APISet::addObjCMethod( |
| ObjCContainerRecord *Container, StringRef Name, StringRef USR, |
| PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, |
| DeclarationFragments Declaration, DeclarationFragments SubHeading, |
| FunctionSignature Signature, bool IsInstanceMethod, |
| bool IsFromSystemHeader) { |
| std::unique_ptr<ObjCMethodRecord> Record; |
| if (IsInstanceMethod) |
| Record = std::make_unique<ObjCInstanceMethodRecord>( |
| USR, Name, Loc, std::move(Availabilities), Comment, Declaration, |
| SubHeading, Signature, IsFromSystemHeader); |
| else |
| Record = std::make_unique<ObjCClassMethodRecord>( |
| USR, Name, Loc, std::move(Availabilities), Comment, Declaration, |
| SubHeading, Signature, IsFromSystemHeader); |
| |
| Record->ParentInformation = APIRecord::HierarchyInformation( |
| Container->USR, Container->Name, Container->getKind(), Container); |
| USRBasedLookupTable.insert({USR, Record.get()}); |
| return Container->Methods.emplace_back(std::move(Record)).get(); |
| } |
| |
| ObjCPropertyRecord *APISet::addObjCProperty( |
| ObjCContainerRecord *Container, StringRef Name, StringRef USR, |
| PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, |
| DeclarationFragments Declaration, DeclarationFragments SubHeading, |
| ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName, |
| StringRef SetterName, bool IsOptional, bool IsInstanceProperty, |
| bool IsFromSystemHeader) { |
| std::unique_ptr<ObjCPropertyRecord> Record; |
| if (IsInstanceProperty) |
| Record = std::make_unique<ObjCInstancePropertyRecord>( |
| USR, Name, Loc, std::move(Availabilities), Comment, Declaration, |
| SubHeading, Attributes, GetterName, SetterName, IsOptional, |
| IsFromSystemHeader); |
| else |
| Record = std::make_unique<ObjCClassPropertyRecord>( |
| USR, Name, Loc, std::move(Availabilities), Comment, Declaration, |
| SubHeading, Attributes, GetterName, SetterName, IsOptional, |
| IsFromSystemHeader); |
| Record->ParentInformation = APIRecord::HierarchyInformation( |
| Container->USR, Container->Name, Container->getKind(), Container); |
| USRBasedLookupTable.insert({USR, Record.get()}); |
| return Container->Properties.emplace_back(std::move(Record)).get(); |
| } |
| |
| ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable( |
| ObjCContainerRecord *Container, StringRef Name, StringRef USR, |
| PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, |
| DeclarationFragments Declaration, DeclarationFragments SubHeading, |
| ObjCInstanceVariableRecord::AccessControl Access, bool IsFromSystemHeader) { |
| auto Record = std::make_unique<ObjCInstanceVariableRecord>( |
| USR, Name, Loc, std::move(Availabilities), Comment, Declaration, |
| SubHeading, Access, IsFromSystemHeader); |
| Record->ParentInformation = APIRecord::HierarchyInformation( |
| Container->USR, Container->Name, Container->getKind(), Container); |
| USRBasedLookupTable.insert({USR, Record.get()}); |
| return Container->Ivars.emplace_back(std::move(Record)).get(); |
| } |
| |
| ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR, |
| PresumedLoc Loc, |
| AvailabilitySet Availabilities, |
| const DocComment &Comment, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, ObjCProtocols, USR, Name, Loc, |
| std::move(Availabilities), Comment, Declaration, |
| SubHeading, IsFromSystemHeader); |
| } |
| |
| MacroDefinitionRecord * |
| APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, Macros, USR, Name, Loc, |
| Declaration, SubHeading, IsFromSystemHeader); |
| } |
| |
| TypedefRecord * |
| APISet::addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc, |
| AvailabilitySet Availabilities, const DocComment &Comment, |
| DeclarationFragments Declaration, |
| DeclarationFragments SubHeading, |
| SymbolReference UnderlyingType, bool IsFromSystemHeader) { |
| return addTopLevelRecord(USRBasedLookupTable, Typedefs, USR, Name, Loc, |
| std::move(Availabilities), Comment, Declaration, |
| SubHeading, UnderlyingType, IsFromSystemHeader); |
| } |
| |
| APIRecord *APISet::findRecordForUSR(StringRef USR) const { |
| if (USR.empty()) |
| return nullptr; |
| |
| auto It = USRBasedLookupTable.find(USR); |
| if (It != USRBasedLookupTable.end()) |
| return It->second; |
| return nullptr; |
| } |
| |
| StringRef APISet::recordUSR(const Decl *D) { |
| 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() {} |
| ObjCMethodRecord::~ObjCMethodRecord() {} |
| ObjCPropertyRecord::~ObjCPropertyRecord() {} |
| |
| void GlobalFunctionRecord::anchor() {} |
| void GlobalVariableRecord::anchor() {} |
| void EnumConstantRecord::anchor() {} |
| void EnumRecord::anchor() {} |
| void StructFieldRecord::anchor() {} |
| void StructRecord::anchor() {} |
| void ObjCInstancePropertyRecord::anchor() {} |
| void ObjCClassPropertyRecord::anchor() {} |
| void ObjCInstanceVariableRecord::anchor() {} |
| void ObjCInstanceMethodRecord::anchor() {} |
| void ObjCClassMethodRecord::anchor() {} |
| void ObjCCategoryRecord::anchor() {} |
| void ObjCInterfaceRecord::anchor() {} |
| void ObjCProtocolRecord::anchor() {} |
| void MacroDefinitionRecord::anchor() {} |
| void TypedefRecord::anchor() {} |