diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -77,7 +77,8 @@ /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) enum RecordKind { - RK_Global, + RK_GlobalFunction, + RK_GlobalVariable, RK_EnumConstant, RK_Enum, RK_StructField, @@ -112,31 +113,40 @@ virtual ~APIRecord() = 0; }; -/// The kind of a global record. -enum class GVKind : uint8_t { - Unknown = 0, - Variable = 1, - Function = 2, -}; +/// This holds information associated with global functions. +struct GlobalFunctionRecord : APIRecord { + FunctionSignature Signature; -/// This holds information associated with global variables or functions. -struct GlobalRecord : APIRecord { - GVKind GlobalKind; + GlobalFunctionRecord(StringRef Name, StringRef USR, PresumedLoc Loc, + const AvailabilityInfo &Availability, + LinkageInfo Linkage, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature) + : APIRecord(RK_GlobalFunction, Name, USR, Loc, Availability, Linkage, + Comment, Declaration, SubHeading), + Signature(Signature) {} - /// The function signature of the record if it is a function. - FunctionSignature Signature; + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_GlobalFunction; + } - GlobalRecord(StringRef Name, StringRef USR, PresumedLoc Loc, - const AvailabilityInfo &Availability, LinkageInfo Linkage, - const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading, GVKind Kind, - FunctionSignature Signature) - : APIRecord(RK_Global, Name, USR, Loc, Availability, Linkage, Comment, - Declaration, SubHeading), - GlobalKind(Kind), Signature(Signature) {} +private: + virtual void anchor(); +}; + +/// This holds information associated with global functions. +struct GlobalVariableRecord : APIRecord { + GlobalVariableRecord(StringRef Name, StringRef USR, PresumedLoc Loc, + const AvailabilityInfo &Availability, + LinkageInfo Linkage, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading) + : APIRecord(RK_GlobalVariable, Name, USR, Loc, Availability, Linkage, + Comment, Declaration, SubHeading) {} static bool classof(const APIRecord *Record) { - return Record->getKind() == RK_Global; + return Record->getKind() == RK_GlobalVariable; } private: @@ -446,33 +456,31 @@ virtual void anchor(); }; +/// Check if a record type has a function signature mixin. +/// +/// This is denoted by the record type having a ``Signature`` field of type +/// FunctionSignature. +template +struct has_function_signature : public std::false_type {}; +template <> +struct has_function_signature : public std::true_type {}; +template <> +struct has_function_signature : public std::true_type {}; + /// APISet holds the set of API records collected from given inputs. class APISet { public: - /// Create and add a GlobalRecord of kind \p Kind into the API set. - /// - /// Note: the caller is responsible for keeping the StringRef \p Name and - /// \p USR alive. APISet::copyString provides a way to copy strings into - /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method - /// to generate the USR for \c D and keep it alive in APISet. - GlobalRecord *addGlobal(GVKind Kind, StringRef Name, StringRef USR, - PresumedLoc Loc, const AvailabilityInfo &Availability, - LinkageInfo Linkage, const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading, - FunctionSignature Signature); - /// Create and add a global variable record into the API set. /// /// Note: the caller is responsible for keeping the StringRef \p Name and /// \p USR alive. APISet::copyString provides a way to copy strings into /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method /// to generate the USR for \c D and keep it alive in APISet. - GlobalRecord *addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc, - const AvailabilityInfo &Availability, - LinkageInfo Linkage, const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading); + GlobalVariableRecord * + addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc, + const AvailabilityInfo &Availability, LinkageInfo Linkage, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading); /// Create and add a function record into the API set. /// @@ -480,12 +488,12 @@ /// \p USR alive. APISet::copyString provides a way to copy strings into /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method /// to generate the USR for \c D and keep it alive in APISet. - GlobalRecord *addFunction(StringRef Name, StringRef USR, PresumedLoc Loc, - const AvailabilityInfo &Availability, - LinkageInfo Linkage, const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading, - FunctionSignature Signature); + GlobalFunctionRecord * + addGlobalFunction(StringRef Name, StringRef USR, PresumedLoc Loc, + const AvailabilityInfo &Availability, LinkageInfo Linkage, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature); /// Create and add an enum constant record into the API set. /// @@ -652,7 +660,12 @@ /// Get the language used by the APIs. Language getLanguage() const { return Lang; } - const RecordMap &getGlobals() const { return Globals; } + const RecordMap &getGlobalFunctions() const { + return GlobalFunctions; + } + const RecordMap &getGlobalVariables() const { + return GlobalVariables; + } const RecordMap &getEnums() const { return Enums; } const RecordMap &getStructs() const { return Structs; } const RecordMap &getObjCCategories() const { @@ -699,7 +712,8 @@ const llvm::Triple Target; const Language Lang; - RecordMap Globals; + RecordMap GlobalFunctions; + RecordMap GlobalVariables; RecordMap Enums; RecordMap Structs; RecordMap ObjCCategories; diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -122,7 +122,8 @@ /// /// \returns \c None if this \p Record should be skipped, or a JSON object /// containing common symbol information of \p Record. - Optional serializeAPIRecord(const APIRecord &Record) const; + template + Optional serializeAPIRecord(const RecordTy &Record) const; /// Helper method to serialize second-level member records of \p Record and /// the member-of relationships. @@ -137,8 +138,11 @@ void serializeRelationship(RelationshipKind Kind, SymbolReference Source, SymbolReference Target); - /// Serialize a global record. - void serializeGlobalRecord(const GlobalRecord &Record); + /// Serialize a global function record. + void serializeGlobalFunctionRecord(const GlobalFunctionRecord &Record); + + /// Serialize a global variable record. + void serializeGlobalVariableRecord(const GlobalVariableRecord &Record); /// Serialize an enum record. void serializeEnumRecord(const EnumRecord &Record); diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -41,34 +41,22 @@ } // namespace -GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR, - PresumedLoc Loc, - const AvailabilityInfo &Availability, - LinkageInfo Linkage, const DocComment &Comment, - DeclarationFragments Fragments, - DeclarationFragments SubHeading, - FunctionSignature Signature) { - return addTopLevelRecord(Globals, Name, USR, Loc, Availability, Linkage, - Comment, Fragments, SubHeading, Kind, Signature); -} - -GlobalRecord * +GlobalVariableRecord * APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc, const AvailabilityInfo &Availability, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Fragments, DeclarationFragments SubHeading) { - return addGlobal(GVKind::Variable, Name, USR, Loc, Availability, Linkage, - Comment, Fragments, SubHeading, {}); + return addTopLevelRecord(GlobalVariables, Name, USR, Loc, Availability, + Linkage, Comment, Fragments, SubHeading); } -GlobalRecord * -APISet::addFunction(StringRef Name, StringRef USR, PresumedLoc Loc, - const AvailabilityInfo &Availability, LinkageInfo Linkage, - const DocComment &Comment, DeclarationFragments Fragments, - DeclarationFragments SubHeading, - FunctionSignature Signature) { - return addGlobal(GVKind::Function, Name, USR, Loc, Availability, Linkage, - Comment, Fragments, SubHeading, Signature); +GlobalFunctionRecord *APISet::addGlobalFunction( + StringRef Name, StringRef USR, PresumedLoc Loc, + const AvailabilityInfo &Availability, LinkageInfo Linkage, + const DocComment &Comment, DeclarationFragments Fragments, + DeclarationFragments SubHeading, FunctionSignature Signature) { + return addTopLevelRecord(GlobalFunctions, Name, USR, Loc, Availability, + Linkage, Comment, Fragments, SubHeading, Signature); } EnumConstantRecord *APISet::addEnumConstant( @@ -229,7 +217,8 @@ ObjCContainerRecord::~ObjCContainerRecord() {} -void GlobalRecord::anchor() {} +void GlobalFunctionRecord::anchor() {} +void GlobalVariableRecord::anchor() {} void EnumConstantRecord::anchor() {} void EnumRecord::anchor() {} void StructFieldRecord::anchor() {} diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -198,8 +198,8 @@ DeclarationFragmentsBuilder::getFunctionSignature(Decl); // Add the function record to the API set. - API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration, - SubHeading, Signature); + API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment, + Declaration, SubHeading, Signature); return true; } diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" #include "llvm/Support/VersionTuple.h" +#include using namespace clang; using namespace clang::extractapi; @@ -285,39 +286,6 @@ return Fragments; } -/// Serialize the function signature field of a function, as specified by the -/// Symbol Graph format. -/// -/// The Symbol Graph function signature property contains two arrays. -/// - The \c returns array is the declaration fragments of the return type; -/// - The \c parameters array contains names and declaration fragments of the -/// parameters. -/// -/// \returns \c None if \p FS is empty, or an \c Object containing the -/// formatted function signature. -Optional serializeFunctionSignature(const FunctionSignature &FS) { - if (FS.empty()) - return None; - - Object Signature; - serializeArray(Signature, "returns", - serializeDeclarationFragments(FS.getReturnType())); - - Array Parameters; - for (const auto &P : FS.getParameters()) { - Object Parameter; - Parameter["name"] = P.Name; - serializeArray(Parameter, "declarationFragments", - serializeDeclarationFragments(P.Fragments)); - Parameters.emplace_back(std::move(Parameter)); - } - - if (!Parameters.empty()) - Signature["parameters"] = std::move(Parameters); - - return Signature; -} - /// Serialize the \c names field of a symbol as specified by the Symbol Graph /// format. /// @@ -354,23 +322,14 @@ Object Kind; switch (Record.getKind()) { - case APIRecord::RK_Global: { - auto *GR = dyn_cast(&Record); - switch (GR->GlobalKind) { - case GVKind::Function: - Kind["identifier"] = AddLangPrefix("func"); - Kind["displayName"] = "Function"; - break; - case GVKind::Variable: - Kind["identifier"] = AddLangPrefix("var"); - Kind["displayName"] = "Global Variable"; - break; - case GVKind::Unknown: - // Unknown global kind - break; - } + case APIRecord::RK_GlobalFunction: + Kind["identifier"] = AddLangPrefix("func"); + Kind["displayName"] = "Function"; + break; + case APIRecord::RK_GlobalVariable: + Kind["identifier"] = AddLangPrefix("var"); + Kind["displayName"] = "Global Variable"; break; - } case APIRecord::RK_EnumConstant: Kind["identifier"] = AddLangPrefix("enum.case"); Kind["displayName"] = "Enumeration Case"; @@ -430,6 +389,55 @@ return Kind; } +template +Optional serializeFunctionSignatureMixinImpl(const RecordTy &Record, + std::true_type) { + const auto &FS = Record.Signature; + if (FS.empty()) + return None; + + Object Signature; + serializeArray(Signature, "returns", + serializeDeclarationFragments(FS.getReturnType())); + + Array Parameters; + for (const auto &P : FS.getParameters()) { + Object Parameter; + Parameter["name"] = P.Name; + serializeArray(Parameter, "declarationFragments", + serializeDeclarationFragments(P.Fragments)); + Parameters.emplace_back(std::move(Parameter)); + } + + if (!Parameters.empty()) + Signature["parameters"] = std::move(Parameters); + + return Signature; +} + +template +Optional serializeFunctionSignatureMixinImpl(const RecordTy &Record, + std::false_type) { + return None; +} + +/// Serialize the function signature field, as specified by the +/// Symbol Graph format. +/// +/// The Symbol Graph function signature property contains two arrays. +/// - The \c returns array is the declaration fragments of the return type; +/// - The \c parameters array contains names and declaration fragments of the +/// parameters. +/// +/// \returns \c None if \p FS is empty, or an \c Object containing the +/// formatted function signature. +template +void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) { + serializeObject(Paren, "functionSignature", + serializeFunctionSignatureMixinImpl( + Record, has_function_signature())); +} + } // namespace void SymbolGraphSerializer::anchor() {} @@ -462,8 +470,9 @@ return false; } +template Optional -SymbolGraphSerializer::serializeAPIRecord(const APIRecord &Record) const { +SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { if (shouldSkip(Record)) return None; @@ -485,6 +494,8 @@ Obj["accessLevel"] = "public"; serializeArray(Obj, "pathComponents", Array(PathComponents)); + serializeFunctionSignatureMixin(Obj, Record); + return Obj; } @@ -526,16 +537,24 @@ Relationships.emplace_back(std::move(Relationship)); } -void SymbolGraphSerializer::serializeGlobalRecord(const GlobalRecord &Record) { +void SymbolGraphSerializer::serializeGlobalFunctionRecord( + const GlobalFunctionRecord &Record) { auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); auto Obj = serializeAPIRecord(Record); if (!Obj) return; - if (Record.GlobalKind == GVKind::Function) - serializeObject(*Obj, "functionSignature", - serializeFunctionSignature(Record.Signature)); + Symbols.emplace_back(std::move(*Obj)); +} + +void SymbolGraphSerializer::serializeGlobalVariableRecord( + const GlobalVariableRecord &Record) { + auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); + + auto Obj = serializeAPIRecord(Record); + if (!Obj) + return; Symbols.emplace_back(std::move(*Obj)); } @@ -640,9 +659,12 @@ serializeObject(Root, "metadata", serializeMetadata()); serializeObject(Root, "module", serializeModule()); - // Serialize global records in the API set. - for (const auto &Global : API.getGlobals()) - serializeGlobalRecord(*Global.second); + // Serialize global variables in the API set. + for (const auto &GlobalVar : API.getGlobalVariables()) + serializeGlobalVariableRecord(*GlobalVar.second); + + for (const auto &GlobalFunction : API.getGlobalFunctions()) + serializeGlobalFunctionRecord(*GlobalFunction.second); // Serialize enum records in the API set. for (const auto &Enum : API.getEnums()) diff --git a/clang/test/ExtractAPI/objc_category.m b/clang/test/ExtractAPI/objc_category.m --- a/clang/test/ExtractAPI/objc_category.m +++ b/clang/test/ExtractAPI/objc_category.m @@ -148,6 +148,15 @@ "spelling": ";" } ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, "identifier": { "interfaceLanguage": "objective-c", "precise": "c:objc(cs)Interface(im)InstanceMethod" @@ -212,6 +221,15 @@ "spelling": ";" } ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, "identifier": { "interfaceLanguage": "objective-c", "precise": "c:objc(cs)Interface(cm)ClassMethod" diff --git a/clang/test/ExtractAPI/objc_interface.m b/clang/test/ExtractAPI/objc_interface.m --- a/clang/test/ExtractAPI/objc_interface.m +++ b/clang/test/ExtractAPI/objc_interface.m @@ -182,6 +182,38 @@ "spelling": ";" } ], + "functionSignature": { + "parameters": [ + { + "declarationFragments": [ + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:i", + "spelling": "unsigned int" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "internalParam", + "spelling": "Property" + } + ], + "name": "Property" + } + ], + "returns": [ + { + "kind": "keyword", + "spelling": "id" + } + ] + }, "identifier": { "interfaceLanguage": "objective-c", "precise": "c:objc(cs)Super(cm)getWithProperty:" @@ -288,6 +320,61 @@ "spelling": ";" } ], + "functionSignature": { + "parameters": [ + { + "declarationFragments": [ + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:i", + "spelling": "unsigned int" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "internalParam", + "spelling": "Property" + } + ], + "name": "Property" + }, + { + "declarationFragments": [ + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:i", + "spelling": "unsigned int" + }, + { + "kind": "text", + "spelling": ") " + }, + { + "kind": "internalParam", + "spelling": "Thing" + } + ], + "name": "Thing" + } + ], + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, "identifier": { "interfaceLanguage": "objective-c", "precise": "c:objc(cs)Super(im)setProperty:andOtherThing:" @@ -551,6 +638,15 @@ "spelling": ";" } ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:C", + "spelling": "char" + } + ] + }, "identifier": { "interfaceLanguage": "objective-c", "precise": "c:objc(cs)Derived(im)getIvar"