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