[clang][extract-api] Refactor ExtractAPI and improve docs
- The name SymbolGraph is inappropriate and confusing for the new library for clang-extract-api. Refactor and rename things to make it clear that ExtractAPI is the core functionality and SymbolGraph is one serializer for the API information. - Add documentation comments to ExtractAPI classes and methods to improve readability and clearness of the ExtractAPI work. Differential Revision: https://reviews.llvm.org/D122160
This commit is contained in:
@@ -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 <memory>
|
||||
|
||||
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<RawComment::CommentLine>;
|
||||
|
||||
/// 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 @@ public:
|
||||
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 @@ struct GlobalRecord : APIRecord {
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
using APIRecordUniquePtr =
|
||||
std::unique_ptr<T, UniquePtrBumpPtrAllocatorDeleter<T>>;
|
||||
|
||||
/// A map to store the set of GlobalRecord%s with the declaration name as the
|
||||
/// key.
|
||||
using GlobalRecordMap =
|
||||
llvm::MapVector<StringRef, APIRecordUniquePtr<GlobalRecord>>;
|
||||
|
||||
/// 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
|
||||
@@ -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 @@ struct AvailabilityInfo {
|
||||
bool UnconditionallyDeprecated{false};
|
||||
bool UnconditionallyUnavailable{false};
|
||||
|
||||
explicit AvailabilityInfo(bool Unavailable = false)
|
||||
: Unavailable(Unavailable) {}
|
||||
/// 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) {}
|
||||
|
||||
bool isDefault() const { return *this == AvailabilityInfo(); }
|
||||
bool isUnavailable() const { return Unavailable; }
|
||||
bool isUnconditionallyDeprecated() const { return UnconditionallyDeprecated; }
|
||||
bool isUnconditionallyUnavailable() const {
|
||||
return UnconditionallyUnavailable;
|
||||
}
|
||||
|
||||
friend bool operator==(const AvailabilityInfo &Lhs,
|
||||
const AvailabilityInfo &Rhs);
|
||||
};
|
||||
@@ -60,7 +72,7 @@ inline bool operator==(const AvailabilityInfo &Lhs,
|
||||
Rhs.UnconditionallyUnavailable);
|
||||
}
|
||||
|
||||
} // namespace symbolgraph
|
||||
} // namespace extractapi
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_SYMBOLGRAPH_AVAILABILITY_INFO_H
|
||||
#endif // LLVM_CLANG_EXTRACTAPI_AVAILABILITY_INFO_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 <vector>
|
||||
|
||||
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 @@ public:
|
||||
|
||||
const std::vector<Fragment> &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 @@ public:
|
||||
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 @@ public:
|
||||
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<Fragment> 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 @@ public:
|
||||
|
||||
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 @@ private:
|
||||
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
|
||||
@@ -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<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
|
||||
@@ -30,4 +31,4 @@ public:
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_SYMBOLGRAPH_FRONTEND_ACTIONS_H
|
||||
#endif // LLVM_CLANG_EXTRACTAPI_FRONTEND_ACTIONS_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
|
||||
@@ -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<Object> 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
|
||||
@@ -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<Object> 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
|
||||
@@ -10,6 +10,7 @@ add_subdirectory(Sema)
|
||||
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(DirectoryWatcher)
|
||||
add_subdirectory(Index)
|
||||
add_subdirectory(IndexSerialization)
|
||||
add_subdirectory(StaticAnalyzer)
|
||||
add_subdirectory(SymbolGraph)
|
||||
add_subdirectory(Format)
|
||||
add_subdirectory(Testing)
|
||||
add_subdirectory(Interpreter)
|
||||
|
||||
@@ -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 @@ GlobalRecord *APISet::addGlobal(GVKind Kind, StringRef Name, StringRef USR,
|
||||
FunctionSignature Signature) {
|
||||
auto Result = Globals.insert({Name, nullptr});
|
||||
if (Result.second) {
|
||||
// Create the record if it does not already exist.
|
||||
auto Record = APIRecordUniquePtr<GlobalRecord>(new (Allocator) GlobalRecord{
|
||||
Kind, Name, USR, Loc, Availability, Linkage, Comment, Fragments,
|
||||
SubHeading, Signature});
|
||||
@@ -65,11 +65,11 @@ StringRef APISet::recordUSR(const Decl *D) {
|
||||
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 @@ StringRef APISet::copyString(StringRef String,
|
||||
return StringRef(reinterpret_cast<const char *>(Ptr), String.size());
|
||||
}
|
||||
|
||||
StringRef APISet::copyString(StringRef String) {
|
||||
return copyString(String, Allocator);
|
||||
}
|
||||
APIRecord::~APIRecord() {}
|
||||
|
||||
} // namespace symbolgraph
|
||||
} // namespace clang
|
||||
void GlobalRecord::anchor() {}
|
||||
@@ -2,11 +2,12 @@ set(LLVM_LINK_COMPONENTS
|
||||
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
|
||||
@@ -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 @@ DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
|
||||
DeclarationFragments::FragmentKind::Identifier);
|
||||
return Fragments;
|
||||
}
|
||||
|
||||
} // namespace symbolgraph
|
||||
} // namespace clang
|
||||
@@ -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<ExtractAPIVisitor> {
|
||||
public:
|
||||
explicit ExtractAPIVisitor(ASTContext &Context)
|
||||
@@ -59,6 +62,7 @@ public:
|
||||
Decl->getTemplateSpecializationKind() == TSK_Undeclared)
|
||||
return true;
|
||||
|
||||
// Collect symbol information.
|
||||
StringRef Name = Decl->getName();
|
||||
StringRef USR = API.recordUSR(Decl);
|
||||
PresumedLoc Loc =
|
||||
@@ -69,11 +73,14 @@ public:
|
||||
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 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Collect symbol information.
|
||||
StringRef Name = Decl->getName();
|
||||
StringRef USR = API.recordUSR(Decl);
|
||||
PresumedLoc Loc =
|
||||
@@ -122,6 +130,8 @@ public:
|
||||
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 @@ public:
|
||||
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<AvailabilityAttr>()) {
|
||||
if (A->getPlatform()->getName() != PlatformName)
|
||||
@@ -174,15 +187,21 @@ public:
|
||||
: 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<raw_pwrite_stream> OS;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ASTConsumer>
|
||||
19
clang/lib/ExtractAPI/Serialization/SerializerBase.cpp
Normal file
19
clang/lib/ExtractAPI/Serialization/SerializerBase.cpp
Normal file
@@ -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) {}
|
||||
450
clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Normal file
450
clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Normal file
@@ -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<Object> 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> 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<Object> 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<Object> serializeAvailability(const AvailabilityInfo &Avail) {
|
||||
if (Avail.isDefault())
|
||||
return None;
|
||||
|
||||
Object Availbility;
|
||||
serializeObject(Availbility, "introducedVersion",
|
||||
serializeSemanticVersion(Avail.Introduced));
|
||||
serializeObject(Availbility, "deprecatedVersion",
|
||||
serializeSemanticVersion(Avail.Deprecated));
|
||||
serializeObject(Availbility, "obsoletedVersion",
|
||||
serializeSemanticVersion(Avail.Obsoleted));
|
||||
if (Avail.isUnavailable())
|
||||
Availbility["isUnconditionallyUnavailable"] = true;
|
||||
if (Avail.isUnconditionallyDeprecated())
|
||||
Availbility["isUnconditionallyDeprecated"] = true;
|
||||
|
||||
return Availbility;
|
||||
}
|
||||
|
||||
/// 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<Object> 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<Array> 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<Object> 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<GlobalRecord>(&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<Object>
|
||||
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";
|
||||
}
|
||||
@@ -7,9 +7,9 @@ set(link_libs
|
||||
clangBasic
|
||||
clangCodeGen
|
||||
clangDriver
|
||||
clangExtractAPI
|
||||
clangFrontend
|
||||
clangRewriteFrontend
|
||||
clangSymbolGraph
|
||||
)
|
||||
|
||||
if(CLANG_ENABLE_ARCMT)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<Object> Obj) {
|
||||
if (Obj)
|
||||
Paren[Key] = std::move(Obj.getValue());
|
||||
}
|
||||
|
||||
static void serializeArray(Object &Paren, StringRef Key,
|
||||
Optional<Array> Array) {
|
||||
if (Array)
|
||||
Paren[Key] = std::move(Array.getValue());
|
||||
}
|
||||
|
||||
// SymbolGraph: SemanticVersion
|
||||
static Optional<Object> 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<Object> serializeAvailability(const AvailabilityInfo &Avail) {
|
||||
if (Avail.isDefault())
|
||||
return None;
|
||||
|
||||
Object Availbility;
|
||||
serializeObject(Availbility, "introducedVersion",
|
||||
serializeSemanticVersion(Avail.Introduced));
|
||||
serializeObject(Availbility, "deprecatedVersion",
|
||||
serializeSemanticVersion(Avail.Deprecated));
|
||||
serializeObject(Availbility, "obsoletedVersion",
|
||||
serializeSemanticVersion(Avail.Obsoleted));
|
||||
if (Avail.isUnavailable())
|
||||
Availbility["isUnconditionallyUnavailable"] = true;
|
||||
if (Avail.isUnconditionallyDeprecated())
|
||||
Availbility["isUnconditionallyDeprecated"] = true;
|
||||
|
||||
return Availbility;
|
||||
}
|
||||
|
||||
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<Object> 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<Array>
|
||||
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<Object>
|
||||
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<GlobalRecord>(&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<Object> 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";
|
||||
}
|
||||
Reference in New Issue
Block a user