diff options
Diffstat (limited to 'clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp')
-rw-r--r-- | clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp | 263 |
1 files changed, 219 insertions, 44 deletions
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp index cea4d91..9122d49 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -12,10 +12,17 @@ //===----------------------------------------------------------------------===// #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" @@ -323,18 +330,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"; @@ -363,23 +368,21 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) { Kind["identifier"] = AddLangPrefix("ivar"); Kind["displayName"] = "Instance Variable"; break; - case APIRecord::RK_ObjCMethod: - if (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: - if (cast<ObjCPropertyRecord>(&Record)->isClassProperty()) { - Kind["identifier"] = AddLangPrefix("type.property"); - Kind["displayName"] = "Type Property"; - } else { - Kind["identifier"] = AddLangPrefix("property"); - Kind["displayName"] = "Instance Property"; - } + 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"); @@ -407,6 +410,15 @@ 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) { @@ -456,6 +468,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() {} @@ -475,7 +559,7 @@ 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; } @@ -519,7 +603,16 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { // 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); @@ -530,8 +623,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; @@ -567,8 +662,6 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, void SymbolGraphSerializer::serializeGlobalFunctionRecord( const GlobalFunctionRecord &Record) { - auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); - auto Obj = serializeAPIRecord(Record); if (!Obj) return; @@ -578,8 +671,6 @@ void SymbolGraphSerializer::serializeGlobalFunctionRecord( void SymbolGraphSerializer::serializeGlobalVariableRecord( const GlobalVariableRecord &Record) { - auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); - auto Obj = serializeAPIRecord(Record); if (!Obj) return; @@ -588,7 +679,6 @@ void SymbolGraphSerializer::serializeGlobalVariableRecord( } void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) { - auto EnumPathComponentGuard = makePathComponentGuard(Record.Name); auto Enum = serializeAPIRecord(Record); if (!Enum) return; @@ -598,7 +688,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; @@ -609,7 +698,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; @@ -647,7 +735,6 @@ void SymbolGraphSerializer::serializeObjCContainerRecord( void SymbolGraphSerializer::serializeMacroDefinitionRecord( const MacroDefinitionRecord &Record) { - auto MacroPathComponentGuard = makePathComponentGuard(Record.Name); auto Macro = serializeAPIRecord(Record); if (!Macro) @@ -656,6 +743,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 @@ -667,7 +794,6 @@ void SymbolGraphSerializer::serializeTypedefRecord( if (ShouldDrop) return; - auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name); auto Typedef = serializeAPIRecord(Record); if (!Typedef) return; @@ -677,16 +803,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); @@ -716,6 +833,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); @@ -729,3 +854,53 @@ void SymbolGraphSerializer::serialize(raw_ostream &os) { else os << formatv("{0:2}", Value(std::move(root))) << "\n"; } + +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; +} |