diff --git a/clang/include/clang/SymbolGraph/API.h b/clang/include/clang/ExtractAPI/API.h rename from clang/include/clang/SymbolGraph/API.h rename to clang/include/clang/ExtractAPI/API.h --- a/clang/include/clang/SymbolGraph/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -1,4 +1,4 @@ -//===- SymbolGraph/API.h ----------------------------------------*- C++ -*-===// +//===- ExtractAPI/API.h -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,18 +7,22 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Defines SymbolGraph API records. +/// This file defines the APIRecord-based structs and the APISet class. +/// +/// Clang ExtractAPI is a tool to collect API information from a given set of +/// header files. The structures in this file describe data representations of +/// the API information collected for various kinds of symbols. /// //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SYMBOLGRAPH_API_H -#define LLVM_CLANG_SYMBOLGRAPH_API_H +#ifndef LLVM_CLANG_EXTRACTAPI_API_H +#define LLVM_CLANG_EXTRACTAPI_API_H #include "clang/AST/Decl.h" #include "clang/AST/RawCommentList.h" #include "clang/Basic/SourceLocation.h" -#include "clang/SymbolGraph/AvailabilityInfo.h" -#include "clang/SymbolGraph/DeclarationFragments.h" +#include "clang/ExtractAPI/AvailabilityInfo.h" +#include "clang/ExtractAPI/DeclarationFragments.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" @@ -27,18 +31,43 @@ #include namespace clang { -namespace symbolgraph { +namespace extractapi { +/// DocComment is a vector of RawComment::CommentLine. +/// +/// Each line represents one line of striped documentation comment, +/// with source range information. This simplifies calculating the source +/// location of a character in the doc comment for pointing back to the source +/// file. +/// e.g. +/// \code +/// /// This is a documentation comment +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line. +/// /// with multiple lines. +/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line. +/// \endcode using DocComment = std::vector; +/// The base representation of an API record. Holds common symbol information. struct APIRecord { StringRef Name; StringRef USR; PresumedLoc Location; AvailabilityInfo Availability; LinkageInfo Linkage; + + /// Documentation comment lines attached to this symbol declaration. DocComment Comment; + + /// Declaration fragments of this symbol declaration. DeclarationFragments Declaration; + + /// SubHeading provides a more detailed representation than the plain + /// declaration name. + /// + /// SubHeading is an array of declaration fragments of tagged declaration + /// name, with potentially more tokens (for example the \c +/- symbol for + /// Objective-C class/instance methods). DeclarationFragments SubHeading; /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) @@ -66,14 +95,18 @@ 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 variables or functions. struct GlobalRecord : APIRecord { GVKind GlobalKind; + + /// The function signature of the record if it is a function. FunctionSignature Signature; GlobalRecord(GVKind Kind, StringRef Name, StringRef USR, PresumedLoc Loc, @@ -87,16 +120,20 @@ static bool classof(const APIRecord *Record) { return Record->getKind() == RK_Global; } + +private: + virtual void anchor(); }; +/// APISet holds the set of API records collected from given inputs. class APISet { public: - APISet(const llvm::Triple &Target, const LangOptions &LangOpts) - : Target(Target), LangOpts(LangOpts) {} - - const llvm::Triple &getTarget() const { return Target; } - const LangOptions &getLangOpts() const { return LangOpts; } - + /// 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, @@ -104,12 +141,24 @@ 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); + /// Create and add a function 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 *addFunction(StringRef Name, StringRef USR, PresumedLoc Loc, const AvailabilityInfo &Availability, LinkageInfo Linkage, const DocComment &Comment, @@ -117,10 +166,6 @@ DeclarationFragments SubHeading, FunctionSignature Signature); - StringRef recordUSR(const Decl *D); - StringRef copyString(StringRef String, llvm::BumpPtrAllocator &Allocator); - StringRef copyString(StringRef String); - private: /// \brief A custom deleter used for ``std::unique_ptr`` to APIRecords stored /// in the BumpPtrAllocator. @@ -138,20 +183,45 @@ using APIRecordUniquePtr = std::unique_ptr>; + /// A map to store the set of GlobalRecord%s with the declaration name as the + /// key. using GlobalRecordMap = llvm::MapVector>; + /// Get the target triple for the ExtractAPI invocation. + const llvm::Triple &getTarget() const { return Target; } + + /// Get the language options used to parse the APIs. + const LangOptions &getLangOpts() const { return LangOpts; } + const GlobalRecordMap &getGlobals() const { return Globals; } + /// Generate and store the USR of declaration \p D. + /// + /// Note: The USR string is stored in and owned by Allocator. + /// + /// \returns a StringRef of the generated USR string. + StringRef recordUSR(const Decl *D); + + /// Copy \p String into the Allocator in this APISet. + /// + /// \returns a StringRef of the copied string in APISet::Allocator. + StringRef copyString(StringRef String); + + APISet(const llvm::Triple &Target, const LangOptions &LangOpts) + : Target(Target), LangOpts(LangOpts) {} + private: + /// BumpPtrAllocator to store APIRecord%s and generated/copied strings. llvm::BumpPtrAllocator Allocator; + const llvm::Triple Target; const LangOptions LangOpts; GlobalRecordMap Globals; }; -} // namespace symbolgraph +} // namespace extractapi } // namespace clang -#endif // LLVM_CLANG_SYMBOLGRAPH_API_H +#endif // LLVM_CLANG_EXTRACTAPI_API_H diff --git a/clang/include/clang/SymbolGraph/AvailabilityInfo.h b/clang/include/clang/ExtractAPI/AvailabilityInfo.h rename from clang/include/clang/SymbolGraph/AvailabilityInfo.h rename to clang/include/clang/ExtractAPI/AvailabilityInfo.h --- a/clang/include/clang/SymbolGraph/AvailabilityInfo.h +++ b/clang/include/clang/ExtractAPI/AvailabilityInfo.h @@ -1,4 +1,4 @@ -//===- SymbolGraph/AvailabilityInfo.h - Availability Info -------*- C++ -*-===// +//===- ExtractAPI/AvailabilityInfo.h - Availability Info --------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,12 +7,13 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Defines the Availability Info for a declaration. +/// This file defines the AvailabilityInfo struct that collects availability +/// attributes of a symbol. /// //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SYMBOLGRAPH_AVAILABILITY_INFO_H -#define LLVM_CLANG_SYMBOLGRAPH_AVAILABILITY_INFO_H +#ifndef LLVM_CLANG_EXTRACTAPI_AVAILABILITY_INFO_H +#define LLVM_CLANG_EXTRACTAPI_AVAILABILITY_INFO_H #include "llvm/Support/Error.h" #include "llvm/Support/VersionTuple.h" @@ -21,8 +22,9 @@ using llvm::VersionTuple; namespace clang { -namespace symbolgraph { +namespace extractapi { +/// Stores availability attributes of a symbol. struct AvailabilityInfo { VersionTuple Introduced; VersionTuple Deprecated; @@ -31,21 +33,31 @@ bool UnconditionallyDeprecated{false}; bool UnconditionallyUnavailable{false}; - explicit AvailabilityInfo(bool Unavailable = false) - : Unavailable(Unavailable) {} - - AvailabilityInfo(VersionTuple I, VersionTuple D, VersionTuple O, bool U, - bool UD, bool UU) - : Introduced(I), Deprecated(D), Obsoleted(O), Unavailable(U), - UnconditionallyDeprecated(UD), UnconditionallyUnavailable(UU) {} - + /// Determine if this AvailabilityInfo represents the default availability. bool isDefault() const { return *this == AvailabilityInfo(); } + + /// Check if the symbol is unavailable. bool isUnavailable() const { return Unavailable; } + + /// Check if the symbol is unconditionally deprecated. + /// + /// i.e. \code __attribute__((deprecated)) \endcode bool isUnconditionallyDeprecated() const { return UnconditionallyDeprecated; } + + /// Check if the symbol is unconditionally unavailable. + /// + /// i.e. \code __attribute__((unavailable)) \endcode bool isUnconditionallyUnavailable() const { return UnconditionallyUnavailable; } + AvailabilityInfo() = default; + + AvailabilityInfo(VersionTuple I, VersionTuple D, VersionTuple O, bool U, + bool UD, bool UU) + : Introduced(I), Deprecated(D), Obsoleted(O), Unavailable(U), + UnconditionallyDeprecated(UD), UnconditionallyUnavailable(UU) {} + friend bool operator==(const AvailabilityInfo &Lhs, const AvailabilityInfo &Rhs); }; @@ -60,7 +72,7 @@ Rhs.UnconditionallyUnavailable); } -} // namespace symbolgraph +} // namespace extractapi } // namespace clang -#endif // LLVM_CLANG_SYMBOLGRAPH_AVAILABILITY_INFO_H +#endif // LLVM_CLANG_EXTRACTAPI_AVAILABILITY_INFO_H diff --git a/clang/include/clang/SymbolGraph/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h rename from clang/include/clang/SymbolGraph/DeclarationFragments.h rename to clang/include/clang/ExtractAPI/DeclarationFragments.h --- a/clang/include/clang/SymbolGraph/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -1,4 +1,4 @@ -//===- SymbolGraph/DeclarationFragments.h -----------------------*- C++ -*-===// +//===- ExtractAPI/DeclarationFragments.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,12 +7,16 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Defines SymbolGraph Declaration Fragments related classes. +/// This file defines the Declaration Fragments related classes. +/// +/// Declaration Fragments represent parts of a symbol declaration tagged with +/// syntactic/semantic information. +/// See https://github.com/apple/swift-docc-symbolkit /// //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SYMBOLGRAPH_DECLARATION_FRAGMENTS_H -#define LLVM_CLANG_SYMBOLGRAPH_DECLARATION_FRAGMENTS_H +#ifndef LLVM_CLANG_EXTRACTAPI_DECLARATION_FRAGMENTS_H +#define LLVM_CLANG_EXTRACTAPI_DECLARATION_FRAGMENTS_H #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -21,29 +25,64 @@ #include namespace clang { -namespace symbolgraph { +namespace extractapi { +/// DeclarationFragments is a vector of tagged important parts of a symbol's +/// declaration. +/// +/// The fragments sequence can be joined to form spans of declaration text, with +/// attached information useful for purposes like syntax-highlighting etc. +/// For example: +/// \code +/// const -> keyword "const" +/// int -> type "int" +/// pi; -> identifier "pi" +/// \endcode class DeclarationFragments { public: DeclarationFragments() = default; + /// The kind of a fragment. enum class FragmentKind { + /// Unknown fragment kind. None, + Keyword, Attribute, NumberLiteral, StringLiteral, Identifier, + + /// Identifier that refers to a type in the context. TypeIdentifier, + + /// Parameter that's used as generics in the context. For example template + /// parameters. GenericParameter, + + /// External parameters in Objective-C methods. + /// For example, \c forKey in + /// \code{.m} + /// - (void) setValue:(Value)value forKey(Key)key + /// \endcode ExternalParam, + + /// Internal/local parameters in Objective-C methods. + /// For example, \c key in + /// \code{.m} + /// - (void) setValue:(Value)value forKey(Key)key + /// \endcode InternalParam, + Text, }; + /// Fragment holds information of a single fragment. struct Fragment { std::string Spelling; FragmentKind Kind; + + /// The USR of the fragment symbol, if applicable. std::string PreciseIdentifier; Fragment(StringRef Spelling, FragmentKind Kind, StringRef PreciseIdentifier) @@ -53,10 +92,16 @@ const std::vector &getFragments() const { return Fragments; } + /// Append a new Fragment to the end of the Fragments. + /// + /// \returns a reference to the DeclarationFragments object itself after + /// appending to chain up consecutive appends. DeclarationFragments &append(StringRef Spelling, FragmentKind Kind, StringRef PreciseIdentifier = "") { if (Kind == FragmentKind::Text && !Fragments.empty() && Fragments.back().Kind == FragmentKind::Text) { + // If appending a text fragment, and the last fragment is also text, + // merge into the last fragment. Fragments.back().Spelling.append(Spelling.data(), Spelling.size()); } else { Fragments.emplace_back(Spelling, Kind, PreciseIdentifier); @@ -64,6 +109,13 @@ return *this; } + /// Append another DeclarationFragments to the end. + /// + /// Note: \p Other is moved from and cannot be used after a call to this + /// method. + /// + /// \returns a reference to the DeclarationFragments object itself after + /// appending to chain up consecutive appends. DeclarationFragments &append(DeclarationFragments &&Other) { Fragments.insert(Fragments.end(), std::make_move_iterator(Other.Fragments.begin()), @@ -72,19 +124,29 @@ return *this; } + /// Append a text Fragment of a space character. + /// + /// \returns a reference to the DeclarationFragments object itself after + /// appending to chain up consecutive appends. DeclarationFragments &appendSpace(); + /// Get the string description of a FragmentKind \p Kind. static StringRef getFragmentKindString(FragmentKind Kind); + + /// Get the corresponding FragmentKind from string \p S. static FragmentKind parseFragmentKindFromString(StringRef S); private: std::vector Fragments; }; +/// Store function signature information with DeclarationFragments of the +/// return type and parameters. class FunctionSignature { public: FunctionSignature() = default; + /// Parameter holds the name and DeclarationFragments of a single parameter. struct Parameter { std::string Name; DeclarationFragments Fragments; @@ -104,6 +166,10 @@ void setReturnType(DeclarationFragments RT) { ReturnType = RT; } + /// Determine if the FunctionSignature is empty. + /// + /// \returns true if the return type DeclarationFragments is empty and there + /// is no parameter, otherwise false. bool empty() const { return Parameters.empty() && ReturnType.getFragments().empty(); } @@ -113,28 +179,46 @@ DeclarationFragments ReturnType; }; +/// A factory class to build DeclarationFragments for different kinds of Decl. class DeclarationFragmentsBuilder { public: + /// Build DeclarationFragments for a variable declaration VarDecl. static DeclarationFragments getFragmentsForVar(const VarDecl *); + + /// Build DeclarationFragments for a function declaration FunctionDecl. static DeclarationFragments getFragmentsForFunction(const FunctionDecl *); + + /// Build sub-heading fragments for a NamedDecl. static DeclarationFragments getSubHeading(const NamedDecl *); + + /// Build FunctionSignature for a function declaration FunctionDecl. static FunctionSignature getFunctionSignature(const FunctionDecl *); private: DeclarationFragmentsBuilder() = delete; + /// Build DeclarationFragments for a QualType. static DeclarationFragments getFragmentsForType(const QualType, ASTContext &, DeclarationFragments &); + + /// Build DeclarationFragments for a Type. static DeclarationFragments getFragmentsForType(const Type *, ASTContext &, DeclarationFragments &); + + /// Build DeclarationFragments for a NestedNameSpecifier. static DeclarationFragments getFragmentsForNNS(const NestedNameSpecifier *, ASTContext &, DeclarationFragments &); + + /// Build DeclarationFragments for Qualifiers. static DeclarationFragments getFragmentsForQualifiers(const Qualifiers quals); + + /// Build DeclarationFragments for a parameter variable declaration + /// ParmVarDecl. static DeclarationFragments getFragmentsForParam(const ParmVarDecl *); }; -} // namespace symbolgraph +} // namespace extractapi } // namespace clang -#endif // LLVM_CLANG_SYMBOLGRAPH_DECLARATION_FRAGMENTS_H +#endif // LLVM_CLANG_EXTRACTAPI_DECLARATION_FRAGMENTS_H diff --git a/clang/include/clang/SymbolGraph/FrontendActions.h b/clang/include/clang/ExtractAPI/FrontendActions.h rename from clang/include/clang/SymbolGraph/FrontendActions.h rename to clang/include/clang/ExtractAPI/FrontendActions.h --- a/clang/include/clang/SymbolGraph/FrontendActions.h +++ b/clang/include/clang/ExtractAPI/FrontendActions.h @@ -1,4 +1,4 @@ -//===- SymbolGraph/FrontendActions.h -----------------------*- C++ -*-===// +//===- ExtractAPI/FrontendActions.h -----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,17 +7,18 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Defines SymbolGraph frontend actions. +/// This file defines the ExtractAPIAction frontend action. /// //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SYMBOLGRAPH_FRONTEND_ACTIONS_H -#define LLVM_CLANG_SYMBOLGRAPH_FRONTEND_ACTIONS_H +#ifndef LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H +#define LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H #include "clang/Frontend/FrontendAction.h" namespace clang { +/// ExtractAPIAction sets up the output file and creates the ExtractAPIVisitor. class ExtractAPIAction : public ASTFrontendAction { protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, @@ -30,4 +31,4 @@ } // namespace clang -#endif // LLVM_CLANG_SYMBOLGRAPH_FRONTEND_ACTIONS_H +#endif // LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_H diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -0,0 +1,56 @@ +//===- ExtractAPI/Serialization/SerializerBase.h ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the ExtractAPI APISerializer interface. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H +#define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H + +#include "clang/ExtractAPI/API.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace extractapi { + +/// Common options to customize the serializer output. +struct APISerializerOption { + /// Do not include unnecessary whitespaces to save space. + bool Compact; +}; + +/// The base interface of serializers for API information. +class APISerializer { +public: + /// Serialize the API information to \p os. + virtual void serialize(raw_ostream &os) = 0; + +protected: + const APISet &API; + APISerializerOption Options; + +public: + APISerializer() = delete; + APISerializer(const APISerializer &) = delete; + APISerializer(APISerializer &&) = delete; + APISerializer &operator=(const APISerializer &) = delete; + APISerializer &operator=(APISerializer &&) = delete; + +protected: + APISerializer(const APISet &API, APISerializerOption Options = {}) + : API(API), Options(Options) {} + + virtual ~APISerializer() = default; +}; + +} // namespace extractapi +} // namespace clang + +#endif // LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -0,0 +1,100 @@ +//===- ExtractAPI/Serialization/SymbolGraphSerializer.h ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the SymbolGraphSerializer class. +/// +/// Implement an APISerializer for the Symbol Graph format for ExtractAPI. +/// See https://github.com/apple/swift-docc-symbolkit. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H +#define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H + +#include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/Serialization/SerializerBase.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace extractapi { + +using namespace llvm::json; + +/// The serializer that organizes API information in the Symbol Graph format. +/// +/// The Symbol Graph format (https://github.com/apple/swift-docc-symbolkit) +/// models an API set as a directed graph, where nodes are symbol declarations, +/// and edges are relationships between the connected symbols. +class SymbolGraphSerializer : public APISerializer { + virtual void anchor(); + + /// A JSON array of formatted symbols in \c APISet. + Array Symbols; + + /// A JSON array of formatted symbol relationships in \c APISet. + Array Relationships; + + /// The Symbol Graph format version used by this serializer. + static const VersionTuple FormatVersion; + +public: + /// Serialize the APIs in \c APISet in the Symbol Graph format. + /// + /// \returns a JSON object that contains the root of the formatted + /// Symbol Graph. + Object serialize(); + + /// Implement the APISerializer::serialize interface. Wrap serialize(void) and + /// write out the serialized JSON object to \p os. + void serialize(raw_ostream &os) override; + +private: + /// Synthesize the metadata section of the Symbol Graph format. + /// + /// The metadata section describes information about the Symbol Graph itself, + /// including the format version and the generator information. + Object serializeMetadata() const; + + /// Synthesize the module section of the Symbol Graph format. + /// + /// The module section contains information about the product that is defined + /// by the given API set. + /// Note that "module" here is not to be confused with the Clang/C++ module + /// concept. + Object serializeModule() const; + + /// Determine if the given \p Record should be skipped during serialization. + bool shouldSkip(const APIRecord &Record) const; + + /// Format the common API information for \p Record. + /// + /// This handles the shared information of all kinds of API records, + /// for example identifier and source location. The resulting object is then + /// augmented with kind-specific symbol information by the caller. + /// This method also checks if the given \p Record should be skipped during + /// serialization. + /// + /// \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; + + /// Serialize a global record. + void serializeGlobalRecord(const GlobalRecord &Record); + +public: + SymbolGraphSerializer(const APISet &API, APISerializerOption Options = {}) + : APISerializer(API, Options) {} +}; + +} // namespace extractapi +} // namespace clang + +#endif // LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SYMBOLGRAPHSERIALIZER_H diff --git a/clang/include/clang/SymbolGraph/Serialization.h b/clang/include/clang/SymbolGraph/Serialization.h deleted file mode 100644 --- a/clang/include/clang/SymbolGraph/Serialization.h +++ /dev/null @@ -1,58 +0,0 @@ -//===- SymbolGraph/Serialization.h ------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// \brief Defines the SymbolGraph serializer and parser. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_SYMBOLGRAPH_SERIALIZATION_H -#define LLVM_CLANG_SYMBOLGRAPH_SERIALIZATION_H - -#include "clang/SymbolGraph/API.h" -#include "llvm/Support/JSON.h" -#include "llvm/Support/VersionTuple.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -namespace symbolgraph { - -using namespace llvm::json; - -struct SerializerOption { - bool Compact; -}; - -class Serializer { -public: - Serializer(const APISet &API, SerializerOption Options = {}) - : API(API), Options(Options) {} - - Object serialize(); - void serialize(raw_ostream &os); - -private: - Object serializeMetadata() const; - Object serializeModule() const; - Optional serializeAPIRecord(const APIRecord &Record) const; - void serializeGlobalRecord(const GlobalRecord &Record); - - bool shouldSkip(const APIRecord &Record) const; - - const APISet &API; - SerializerOption Options; - Array Symbols; - Array Relationships; - - static const VersionTuple FormatVersion; -}; - -} // namespace symbolgraph -} // namespace clang - -#endif // LLVM_CLANG_SYMBOLGRAPH_SERIALIZATION_H diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(CodeGen) add_subdirectory(Analysis) add_subdirectory(Edit) +add_subdirectory(ExtractAPI) add_subdirectory(Rewrite) if(CLANG_ENABLE_ARCMT) add_subdirectory(ARCMigrate) @@ -23,7 +24,6 @@ add_subdirectory(Index) add_subdirectory(IndexSerialization) add_subdirectory(StaticAnalyzer) -add_subdirectory(SymbolGraph) add_subdirectory(Format) add_subdirectory(Testing) add_subdirectory(Interpreter) diff --git a/clang/lib/SymbolGraph/API.cpp b/clang/lib/ExtractAPI/API.cpp rename from clang/lib/SymbolGraph/API.cpp rename to clang/lib/ExtractAPI/API.cpp --- a/clang/lib/SymbolGraph/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -1,4 +1,4 @@ -//===- SymbolGraph/API.cpp --------------------------------------*- C++ -*-===// +//===- ExtractAPI/API.cpp ---------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,21 +7,20 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Defines SymbolGraph API records. +/// This file implements the APIRecord and derived record structs, +/// and the APISet class. /// //===----------------------------------------------------------------------===// -#include "clang/SymbolGraph/API.h" +#include "clang/ExtractAPI/API.h" #include "clang/AST/CommentCommandTraits.h" #include "clang/AST/CommentLexer.h" #include "clang/AST/RawCommentList.h" #include "clang/Index/USRGeneration.h" #include "llvm/Support/Allocator.h" -namespace clang { -namespace symbolgraph { - -APIRecord::~APIRecord() {} +using namespace clang::extractapi; +using namespace llvm; GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR, PresumedLoc Loc, @@ -32,6 +31,7 @@ FunctionSignature Signature) { auto Result = Globals.insert({Name, nullptr}); if (Result.second) { + // Create the record if it does not already exist. auto Record = APIRecordUniquePtr(new (Allocator) GlobalRecord{ Kind, Name, USR, Loc, Availability, Linkage, Comment, Fragments, SubHeading, Signature}); @@ -65,11 +65,11 @@ return copyString(USR); } -StringRef APISet::copyString(StringRef String, - llvm::BumpPtrAllocator &Allocator) { +StringRef APISet::copyString(StringRef String) { if (String.empty()) return {}; + // No need to allocate memory and copy if the string has already been stored. if (Allocator.identifyObject(String.data())) return String; @@ -78,9 +78,6 @@ return StringRef(reinterpret_cast(Ptr), String.size()); } -StringRef APISet::copyString(StringRef String) { - return copyString(String, Allocator); -} +APIRecord::~APIRecord() {} -} // namespace symbolgraph -} // namespace clang +void GlobalRecord::anchor() {} diff --git a/clang/lib/SymbolGraph/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt rename from clang/lib/SymbolGraph/CMakeLists.txt rename to clang/lib/ExtractAPI/CMakeLists.txt --- a/clang/lib/SymbolGraph/CMakeLists.txt +++ b/clang/lib/ExtractAPI/CMakeLists.txt @@ -2,11 +2,12 @@ Support ) -add_clang_library(clangSymbolGraph +add_clang_library(clangExtractAPI API.cpp ExtractAPIConsumer.cpp DeclarationFragments.cpp - Serialization.cpp + Serialization/SerializerBase.cpp + Serialization/SymbolGraphSerializer.cpp LINK_LIBS clangAST diff --git a/clang/lib/SymbolGraph/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp rename from clang/lib/SymbolGraph/DeclarationFragments.cpp rename to clang/lib/ExtractAPI/DeclarationFragments.cpp --- a/clang/lib/SymbolGraph/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -1,4 +1,4 @@ -//===- SymbolGraph/DeclarationFragments.cpp ---------------------*- C++ -*-===// +//===- ExtractAPI/DeclarationFragments.cpp ----------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,22 +7,24 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Defines SymbolGraph Declaration Fragments related classes. +/// This file implements Declaration Fragments related classes. /// //===----------------------------------------------------------------------===// -#include "clang/SymbolGraph/DeclarationFragments.h" +#include "clang/ExtractAPI/DeclarationFragments.h" #include "clang/Index/USRGeneration.h" #include "llvm/ADT/StringSwitch.h" -namespace clang { -namespace symbolgraph { +using namespace clang::extractapi; +using namespace llvm; DeclarationFragments &DeclarationFragments::appendSpace() { if (!Fragments.empty()) { Fragment Last = Fragments.back(); if (Last.Kind == FragmentKind::Text) { - if (Last.Spelling.back() != ' ') { + // Merge the extra space into the last fragment if the last fragment is + // also text. + if (Last.Spelling.back() != ' ') { // avoid extra trailing spaces. Last.Spelling.push_back(' '); } } else { @@ -429,6 +431,3 @@ DeclarationFragments::FragmentKind::Identifier); return Fragments; } - -} // namespace symbolgraph -} // namespace clang diff --git a/clang/lib/SymbolGraph/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp rename from clang/lib/SymbolGraph/ExtractAPIConsumer.cpp rename to clang/lib/ExtractAPI/ExtractAPIConsumer.cpp --- a/clang/lib/SymbolGraph/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -1,4 +1,4 @@ -//===- ExtractAPIConsumer.cpp -----------------------------------*- C++ -*-===// +//===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,10 +7,10 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Defines the ExtractAPI AST visitor to collect API information. +/// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to +/// collect API information. /// //===----------------------------------------------------------------------===// -// #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -20,19 +20,22 @@ #include "clang/AST/RawCommentList.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/TargetInfo.h" +#include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/AvailabilityInfo.h" +#include "clang/ExtractAPI/DeclarationFragments.h" +#include "clang/ExtractAPI/FrontendActions.h" +#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/SymbolGraph/API.h" -#include "clang/SymbolGraph/AvailabilityInfo.h" -#include "clang/SymbolGraph/DeclarationFragments.h" -#include "clang/SymbolGraph/FrontendActions.h" -#include "clang/SymbolGraph/Serialization.h" #include "llvm/Support/raw_ostream.h" using namespace clang; -using namespace symbolgraph; +using namespace extractapi; namespace { + +/// The RecursiveASTVisitor to traverse symbol declarations and collect API +/// information. class ExtractAPIVisitor : public RecursiveASTVisitor { public: explicit ExtractAPIVisitor(ASTContext &Context) @@ -59,6 +62,7 @@ Decl->getTemplateSpecializationKind() == TSK_Undeclared) return true; + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = @@ -69,11 +73,14 @@ 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; @@ -112,6 +119,7 @@ return true; } + // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = @@ -122,6 +130,8 @@ 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 = @@ -129,16 +139,19 @@ FunctionSignature Signature = DeclarationFragmentsBuilder::getFunctionSignature(Decl); + // Add the function record to the API set. API.addFunction(Name, USR, Loc, Availability, Linkage, Comment, Declaration, SubHeading, Signature); 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()) { if (A->getPlatform()->getName() != PlatformName) @@ -174,15 +187,21 @@ : Visitor(Context), OS(std::move(OS)) {} void HandleTranslationUnit(ASTContext &Context) override { + // Use ExtractAPIVisitor to traverse symbol declarations in the context. Visitor.TraverseDecl(Context.getTranslationUnitDecl()); - Serializer Serializer(Visitor.getAPI()); - Serializer.serialize(*OS); + + // Setup a SymbolGraphSerializer to write out collected API information in + // the Symbol Graph format. + // FIXME: Make the kind of APISerializer configurable. + SymbolGraphSerializer SGSerializer(Visitor.getAPI()); + SGSerializer.serialize(*OS); } private: ExtractAPIVisitor Visitor; std::unique_ptr OS; }; + } // namespace std::unique_ptr diff --git a/clang/lib/ExtractAPI/Serialization/SerializerBase.cpp b/clang/lib/ExtractAPI/Serialization/SerializerBase.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/ExtractAPI/Serialization/SerializerBase.cpp @@ -0,0 +1,19 @@ +//===- ExtractAPI/Serialization/SerializerBase.cpp --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the APISerializer interface. +/// +//===----------------------------------------------------------------------===// + +#include "clang/ExtractAPI/Serialization/SerializerBase.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang::extractapi; + +void APISerializer::serialize(llvm::raw_ostream &os) {} diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -0,0 +1,450 @@ +//===- ExtractAPI/Serialization/SymbolGraphSerializer.cpp -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the SymbolGraphSerializer. +/// +//===----------------------------------------------------------------------===// + +#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" +#include "clang/Basic/Version.h" +#include "clang/ExtractAPI/API.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/VersionTuple.h" + +using namespace clang; +using namespace clang::extractapi; +using namespace llvm; +using namespace llvm::json; + +namespace { + +/// Helper function to inject a JSON object \p Obj into another object \p Paren +/// at position \p Key. +void serializeObject(Object &Paren, StringRef Key, Optional Obj) { + if (Obj) + Paren[Key] = std::move(Obj.getValue()); +} + +/// Helper function to inject a JSON array \p Array into object \p Paren at +/// position \p Key. +void serializeArray(Object &Paren, StringRef Key, Optional Array) { + if (Array) + Paren[Key] = std::move(Array.getValue()); +} + +/// Serialize a \c VersionTuple \p V with the Symbol Graph semantic version +/// format. +/// +/// A semantic version object contains three numeric fields, representing the +/// \c major, \c minor, and \c patch parts of the version tuple. +/// For example version tuple 1.0.3 is serialized as: +/// \code +/// { +/// "major" : 1, +/// "minor" : 0, +/// "patch" : 3 +/// } +/// \endcode +/// +/// \returns \c None if the version \p V is empty, or an \c Object containing +/// the semantic version representation of \p V. +Optional serializeSemanticVersion(const VersionTuple &V) { + if (V.empty()) + return None; + + Object Version; + Version["major"] = V.getMajor(); + Version["minor"] = V.getMinor().getValueOr(0); + Version["patch"] = V.getSubminor().getValueOr(0); + return Version; +} + +/// Serialize the OS information in the Symbol Graph platform property. +/// +/// The OS information in Symbol Graph contains the \c name of the OS, and an +/// optional \c minimumVersion semantic version field. +Object serializeOperatingSystem(const Triple &T) { + Object OS; + OS["name"] = T.getOSTypeName(T.getOS()); + serializeObject(OS, "minimumVersion", + serializeSemanticVersion(T.getMinimumSupportedOSVersion())); + return OS; +} + +/// Serialize the platform information in the Symbol Graph module section. +/// +/// The platform object describes a target platform triple in corresponding +/// three fields: \c architecture, \c vendor, and \c operatingSystem. +Object serializePlatform(const Triple &T) { + Object Platform; + Platform["architecture"] = T.getArchName(); + Platform["vendor"] = T.getVendorName(); + Platform["operatingSystem"] = serializeOperatingSystem(T); + return Platform; +} + +/// Serialize a source location in file. +/// +/// \param Loc The presumed location to serialize. +/// \param IncludeFileURI If true, include the file path of \p Loc as a URI. +/// Defaults to false. +Object serializeSourcePosition(const PresumedLoc &Loc, + bool IncludeFileURI = false) { + assert(Loc.isValid() && "invalid source position"); + + Object SourcePosition; + SourcePosition["line"] = Loc.getLine(); + SourcePosition["character"] = Loc.getColumn(); + + if (IncludeFileURI) { + std::string FileURI = "file://"; + // Normalize file path to use forward slashes for the URI. + FileURI += sys::path::convert_to_slash(Loc.getFilename()); + SourcePosition["uri"] = FileURI; + } + + return SourcePosition; +} + +/// Serialize a source range with begin and end locations. +Object serializeSourceRange(const PresumedLoc &BeginLoc, + const PresumedLoc &EndLoc) { + Object SourceRange; + serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc)); + serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc)); + return SourceRange; +} + +/// Serialize the availability attributes of a symbol. +/// +/// Availability information contains the introduced, deprecated, and obsoleted +/// versions of the symbol as semantic versions, if not default. +/// Availability information also contains flags to indicate if the symbol is +/// unconditionally unavailable or deprecated, +/// i.e. \c __attribute__((unavailable)) and \c __attribute__((deprecated)). +/// +/// \returns \c None if the symbol has default availability attributes, or +/// an \c Object containing the formatted availability information. +Optional serializeAvailability(const AvailabilityInfo &Avail) { + if (Avail.isDefault()) + return None; + + Object Availbility; + serializeObject(Availbility, "introducedVersion", + serializeSemanticVersion(Avail.Introduced)); + serializeObject(Availbility, "deprecatedVersion", + serializeSemanticVersion(Avail.Deprecated)); + serializeObject(Availbility, "obsoletedVersion", + serializeSemanticVersion(Avail.Obsoleted)); + if (Avail.isUnavailable()) + Availbility["isUnconditionallyUnavailable"] = true; + if (Avail.isUnconditionallyDeprecated()) + Availbility["isUnconditionallyDeprecated"] = true; + + return Availbility; +} + +/// Get the short language name string for interface language references. +StringRef getLanguageName(const LangOptions &LangOpts) { + auto Language = + LangStandard::getLangStandardForKind(LangOpts.LangStd).getLanguage(); + switch (Language) { + case Language::C: + return "c"; + case Language::ObjC: + return "objc"; + + // Unsupported language currently + case Language::CXX: + case Language::ObjCXX: + case Language::OpenCL: + case Language::OpenCLCXX: + case Language::CUDA: + case Language::RenderScript: + case Language::HIP: + + // Languages that the frontend cannot parse and compile + case Language::Unknown: + case Language::Asm: + case Language::LLVM_IR: + llvm_unreachable("Unsupported language kind"); + } + + llvm_unreachable("Unhandled language kind"); +} + +/// Serialize the identifier object as specified by the Symbol Graph format. +/// +/// The identifier property of a symbol contains the USR for precise and unique +/// references, and the interface language name. +Object serializeIdentifier(const APIRecord &Record, + const LangOptions &LangOpts) { + Object Identifier; + Identifier["precise"] = Record.USR; + Identifier["interfaceLanguage"] = getLanguageName(LangOpts); + + return Identifier; +} + +/// Serialize the documentation comments attached to a symbol, as specified by +/// the Symbol Graph format. +/// +/// The Symbol Graph \c docComment object contains an array of lines. Each line +/// represents one line of striped documentation comment, with source range +/// information. +/// e.g. +/// \code +/// /// This is a documentation comment +/// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' First line. +/// /// with multiple lines. +/// ^~~~~~~~~~~~~~~~~~~~~~~' Second line. +/// \endcode +/// +/// \returns \c None if \p Comment is empty, or an \c Object containing the +/// formatted lines. +Optional serializeDocComment(const DocComment &Comment) { + if (Comment.empty()) + return None; + + Object DocComment; + Array LinesArray; + for (const auto &CommentLine : Comment) { + Object Line; + Line["text"] = CommentLine.Text; + serializeObject(Line, "range", + serializeSourceRange(CommentLine.Begin, CommentLine.End)); + LinesArray.emplace_back(std::move(Line)); + } + serializeArray(DocComment, "lines", LinesArray); + + return DocComment; +} + +/// Serialize the declaration fragments of a symbol. +/// +/// The Symbol Graph declaration fragments is an array of tagged important +/// parts of a symbol's declaration. The fragments sequence can be joined to +/// form spans of declaration text, with attached information useful for +/// purposes like syntax-highlighting etc. For example: +/// \code +/// const int pi; -> "declarationFragments" : [ +/// { +/// "kind" : "keyword", +/// "spelling" : "const" +/// }, +/// { +/// "kind" : "text", +/// "spelling" : " " +/// }, +/// { +/// "kind" : "typeIdentifier", +/// "preciseIdentifier" : "c:I", +/// "spelling" : "int" +/// }, +/// { +/// "kind" : "text", +/// "spelling" : " " +/// }, +/// { +/// "kind" : "identifier", +/// "spelling" : "pi" +/// } +/// ] +/// \endcode +/// +/// \returns \c None if \p DF is empty, or an \c Array containing the formatted +/// declaration fragments array. +Optional serializeDeclarationFragments(const DeclarationFragments &DF) { + if (DF.getFragments().empty()) + return None; + + Array Fragments; + for (const auto &F : DF.getFragments()) { + Object Fragment; + Fragment["spelling"] = F.Spelling; + Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind); + if (!F.PreciseIdentifier.empty()) + Fragment["preciseIdentifier"] = F.PreciseIdentifier; + Fragments.emplace_back(std::move(Fragment)); + } + + return Fragments; +} + +/// Serialize the 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. +/// +/// The Symbol Graph names field contains multiple representations of a symbol +/// that can be used for different applications: +/// - \c title : The simple declared name of the symbol; +/// - \c subHeading : An array of declaration fragments that provides tags, +/// and potentially more tokens (for example the \c +/- symbol for +/// Objective-C methods). Can be used as sub-headings for documentation. +Object serializeNames(const APIRecord &Record) { + Object Names; + Names["title"] = Record.Name; + serializeArray(Names, "subHeading", + serializeDeclarationFragments(Record.SubHeading)); + + 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, + const LangOptions &LangOpts) { + Object Kind; + switch (Record.getKind()) { + case APIRecord::RK_Global: + auto *GR = dyn_cast(&Record); + switch (GR->GlobalKind) { + case GVKind::Function: + Kind["identifier"] = (getLanguageName(LangOpts) + ".func").str(); + Kind["displayName"] = "Function"; + break; + case GVKind::Variable: + Kind["identifier"] = (getLanguageName(LangOpts) + ".var").str(); + Kind["displayName"] = "Global Variable"; + break; + case GVKind::Unknown: + // Unknown global kind + break; + } + break; + } + + return Kind; +} + +} // namespace + +void SymbolGraphSerializer::anchor() {} + +/// Defines the format version emitted by SymbolGraphSerializer. +const VersionTuple SymbolGraphSerializer::FormatVersion{0, 5, 3}; + +Object SymbolGraphSerializer::serializeMetadata() const { + Object Metadata; + serializeObject(Metadata, "formatVersion", + serializeSemanticVersion(FormatVersion)); + Metadata["generator"] = clang::getClangFullVersion(); + return Metadata; +} + +Object SymbolGraphSerializer::serializeModule() const { + Object Module; + // FIXME: We might not be building a module, some Clang-based languages might + // not have a "module" concept. Figure out a way to provide a name to + // describe the API set. + Module["name"] = ""; + serializeObject(Module, "platform", serializePlatform(API.getTarget())); + return Module; +} + +bool SymbolGraphSerializer::shouldSkip(const APIRecord &Record) const { + // Skip unconditionally unavailable symbols + if (Record.Availability.isUnconditionallyUnavailable()) + return true; + + return false; +} + +Optional +SymbolGraphSerializer::serializeAPIRecord(const APIRecord &Record) const { + if (shouldSkip(Record)) + return None; + + Object Obj; + serializeObject(Obj, "identifier", + serializeIdentifier(Record, API.getLangOpts())); + serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLangOpts())); + serializeObject(Obj, "names", serializeNames(Record)); + serializeObject( + Obj, "location", + serializeSourcePosition(Record.Location, /*IncludeFileURI=*/true)); + serializeObject(Obj, "availbility", + serializeAvailability(Record.Availability)); + serializeObject(Obj, "docComment", serializeDocComment(Record.Comment)); + serializeArray(Obj, "declarationFragments", + serializeDeclarationFragments(Record.Declaration)); + + return Obj; +} + +void SymbolGraphSerializer::serializeGlobalRecord(const GlobalRecord &Record) { + auto Obj = serializeAPIRecord(Record); + if (!Obj) + return; + + if (Record.GlobalKind == GVKind::Function) + serializeObject(*Obj, "parameters", + serializeFunctionSignature(Record.Signature)); + + Symbols.emplace_back(std::move(*Obj)); +} + +Object SymbolGraphSerializer::serialize() { + Object Root; + serializeObject(Root, "metadata", serializeMetadata()); + serializeObject(Root, "module", serializeModule()); + + // Serialize global records in the API set. + for (const auto &Global : API.getGlobals()) + serializeGlobalRecord(*Global.second); + + Root["symbols"] = std::move(Symbols); + Root["relationhips"] = std::move(Relationships); + + return Root; +} + +void SymbolGraphSerializer::serialize(raw_ostream &os) { + Object root = serialize(); + if (Options.Compact) + os << formatv("{0}", Value(std::move(root))) << "\n"; + else + os << formatv("{0:2}", Value(std::move(root))) << "\n"; +} diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -7,9 +7,9 @@ clangBasic clangCodeGen clangDriver + clangExtractAPI clangFrontend clangRewriteFrontend - clangSymbolGraph ) if(CLANG_ENABLE_ARCMT) diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -15,6 +15,7 @@ #include "clang/CodeGen/CodeGenAction.h" #include "clang/Config/config.h" #include "clang/Driver/Options.h" +#include "clang/ExtractAPI/FrontendActions.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" @@ -25,7 +26,6 @@ #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/AnalyzerHelpFlags.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" -#include "clang/SymbolGraph/FrontendActions.h" #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" #include "llvm/Support/BuryPointer.h" diff --git a/clang/lib/SymbolGraph/Serialization.cpp b/clang/lib/SymbolGraph/Serialization.cpp deleted file mode 100644 --- a/clang/lib/SymbolGraph/Serialization.cpp +++ /dev/null @@ -1,332 +0,0 @@ -//===- SymbolGraph/Serialization.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 -/// \brief Defines the SymbolGraph serializer and parser. -/// -//===----------------------------------------------------------------------===// - -#include "clang/SymbolGraph/Serialization.h" -#include "clang/Basic/Version.h" -#include "clang/SymbolGraph/API.h" -#include "llvm/Support/JSON.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/VersionTuple.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace clang::symbolgraph; -using namespace llvm; -using namespace llvm::json; - -namespace { - -static void serializeObject(Object &Paren, StringRef Key, - Optional Obj) { - if (Obj) - Paren[Key] = std::move(Obj.getValue()); -} - -static void serializeArray(Object &Paren, StringRef Key, - Optional Array) { - if (Array) - Paren[Key] = std::move(Array.getValue()); -} - -// SymbolGraph: SemanticVersion -static Optional serializeSemanticVersion(const VersionTuple &V) { - if (V.empty()) - return None; - - Object Version; - Version["major"] = V.getMajor(); - Version["minor"] = V.getMinor().getValueOr(0); - Version["patch"] = V.getSubminor().getValueOr(0); - return Version; -} - -static Object serializeOperatingSystem(const Triple &T) { - Object OS; - OS["name"] = T.getOSTypeName(T.getOS()); - serializeObject(OS, "minimumVersion", - serializeSemanticVersion(T.getMinimumSupportedOSVersion())); - return OS; -} - -// SymbolGraph: Platform -static Object serializePlatform(const Triple &T) { - Object Platform; - Platform["architecture"] = T.getArchName(); - Platform["vendor"] = T.getVendorName(); - Platform["operatingSystem"] = serializeOperatingSystem(T); - return Platform; -} - -// SymbolGraph: SourcePosition -static Object serializeSourcePosition(const PresumedLoc &Loc, - bool IncludeFileURI = false) { - assert(Loc.isValid() && "invalid source position"); - - Object SourcePosition; - SourcePosition["line"] = Loc.getLine(); - SourcePosition["character"] = Loc.getColumn(); - - if (IncludeFileURI) { - std::string FileURI = "file://"; - FileURI += sys::path::convert_to_slash(Loc.getFilename()); - SourcePosition["uri"] = FileURI; - } - - return SourcePosition; -} - -// SymbolGraph: SourceRange -static Object serializeSourceRange(const PresumedLoc &BeginLoc, - const PresumedLoc &EndLoc) { - Object SourceRange; - serializeObject(SourceRange, "start", serializeSourcePosition(BeginLoc)); - serializeObject(SourceRange, "end", serializeSourcePosition(EndLoc)); - return SourceRange; -} - -// SymbolGraph: AvailabilityItem -static Optional 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; -} - -static StringRef getLanguageName(const LangOptions &LangOpts) { - auto Language = - LangStandard::getLangStandardForKind(LangOpts.LangStd).getLanguage(); - switch (Language) { - case Language::C: - return "c"; - case Language::ObjC: - return "objc"; - - // Unsupported language currently - case Language::CXX: - case Language::ObjCXX: - case Language::OpenCL: - case Language::OpenCLCXX: - case Language::CUDA: - case Language::RenderScript: - case Language::HIP: - - // Languages that the frontend cannot parse and compile - case Language::Unknown: - case Language::Asm: - case Language::LLVM_IR: - llvm_unreachable("Unsupported language kind"); - } - - llvm_unreachable("Unhandled language kind"); -} - -// SymbolGraph: Symbol::identifier -static Object serializeIdentifier(const APIRecord &Record, - const LangOptions &LangOpts) { - Object Identifier; - Identifier["precise"] = Record.USR; - Identifier["interfaceLanguage"] = getLanguageName(LangOpts); - - return Identifier; -} - -// SymbolGraph: DocComment -static Optional serializeDocComment(const DocComment &Comment) { - if (Comment.empty()) - return None; - - Object DocComment; - Array LinesArray; - for (const auto &CommentLine : Comment) { - Object Line; - Line["text"] = CommentLine.Text; - serializeObject(Line, "range", - serializeSourceRange(CommentLine.Begin, CommentLine.End)); - LinesArray.emplace_back(std::move(Line)); - } - serializeArray(DocComment, "lines", LinesArray); - - return DocComment; -} - -static Optional -serializeDeclarationFragments(const DeclarationFragments &DF) { - if (DF.getFragments().empty()) - return None; - - Array Fragments; - for (const auto &F : DF.getFragments()) { - Object Fragment; - Fragment["spelling"] = F.Spelling; - Fragment["kind"] = DeclarationFragments::getFragmentKindString(F.Kind); - if (!F.PreciseIdentifier.empty()) - Fragment["preciseIdentifier"] = F.PreciseIdentifier; - Fragments.emplace_back(std::move(Fragment)); - } - - return Fragments; -} - -static 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; -} - -static Object serializeNames(const APIRecord &Record) { - Object Names; - Names["title"] = Record.Name; - serializeArray(Names, "subHeading", - serializeDeclarationFragments(Record.SubHeading)); - - return Names; -} - -// SymbolGraph: Symbol::kind -static Object serializeSymbolKind(const APIRecord &Record, - const LangOptions &LangOpts) { - Object Kind; - switch (Record.getKind()) { - case APIRecord::RK_Global: - auto *GR = dyn_cast(&Record); - switch (GR->GlobalKind) { - case GVKind::Function: - Kind["identifier"] = (getLanguageName(LangOpts) + ".func").str(); - Kind["displayName"] = "Function"; - break; - case GVKind::Variable: - Kind["identifier"] = (getLanguageName(LangOpts) + ".var").str(); - Kind["displayName"] = "Global Variable"; - break; - case GVKind::Unknown: - // Unknown global kind - break; - } - break; - } - - return Kind; -} - -} // namespace - -const VersionTuple Serializer::FormatVersion{0, 5, 3}; - -Object Serializer::serializeMetadata() const { - Object Metadata; - serializeObject(Metadata, "formatVersion", - serializeSemanticVersion(FormatVersion)); - Metadata["generator"] = clang::getClangFullVersion(); - return Metadata; -} - -Object Serializer::serializeModule() const { - Object Module; - // FIXME: What to put in here? - Module["name"] = ""; - serializeObject(Module, "platform", serializePlatform(API.getTarget())); - return Module; -} - -bool Serializer::shouldSkip(const APIRecord &Record) const { - // Skip unconditionally unavailable symbols - if (Record.Availability.isUnconditionallyUnavailable()) - return true; - - return false; -} - -Optional Serializer::serializeAPIRecord(const APIRecord &Record) const { - if (shouldSkip(Record)) - return None; - - Object Obj; - serializeObject(Obj, "identifier", - serializeIdentifier(Record, API.getLangOpts())); - serializeObject(Obj, "kind", serializeSymbolKind(Record, API.getLangOpts())); - serializeObject(Obj, "names", serializeNames(Record)); - serializeObject( - Obj, "location", - serializeSourcePosition(Record.Location, /*IncludeFileURI=*/true)); - serializeObject(Obj, "availbility", - serializeAvailability(Record.Availability)); - serializeObject(Obj, "docComment", serializeDocComment(Record.Comment)); - serializeArray(Obj, "declarationFragments", - serializeDeclarationFragments(Record.Declaration)); - - return Obj; -} - -void Serializer::serializeGlobalRecord(const GlobalRecord &Record) { - auto Obj = serializeAPIRecord(Record); - if (!Obj) - return; - - if (Record.GlobalKind == GVKind::Function) - serializeObject(*Obj, "parameters", - serializeFunctionSignature(Record.Signature)); - - Symbols.emplace_back(std::move(*Obj)); -} - -Object Serializer::serialize() { - Object Root; - serializeObject(Root, "metadata", serializeMetadata()); - serializeObject(Root, "module", serializeModule()); - - for (const auto &Global : API.getGlobals()) - serializeGlobalRecord(*Global.second); - - Root["symbols"] = std::move(Symbols); - Root["relationhips"] = std::move(Relationships); - - return Root; -} - -void Serializer::serialize(raw_ostream &os) { - Object root = serialize(); - if (Options.Compact) - os << formatv("{0}", Value(std::move(root))) << "\n"; - else - os << formatv("{0:2}", Value(std::move(root))) << "\n"; -} diff --git a/clang/test/SymbolGraph/global_record.c b/clang/test/ExtractAPI/global_record.c rename from clang/test/SymbolGraph/global_record.c rename to clang/test/ExtractAPI/global_record.c