[clang][ExtractAPI] Add support for C++ classes

Add ExtractAPI support C++ classes, fields,  methods, and various qualifiers and specifiers

Differential Revision: https://reviews.llvm.org/D153557
This commit is contained in:
Erick Velez
2023-06-01 10:22:19 -07:00
parent b711d11c83
commit 8b76b44e46
16 changed files with 3261 additions and 46 deletions

View File

@@ -22,6 +22,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/RawCommentList.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "clang/ExtractAPI/AvailabilityInfo.h"
#include "clang/ExtractAPI/DeclarationFragments.h"
#include "llvm/ADT/MapVector.h"
@@ -64,6 +65,14 @@ struct APIRecord {
RK_Enum,
RK_StructField,
RK_Struct,
RK_Union,
RK_StaticField,
RK_CXXField,
RK_CXXClass,
RK_CXXStaticMethod,
RK_CXXInstanceMethod,
RK_CXXConstructorMethod,
RK_CXXDestructorMethod,
RK_ObjCInstanceProperty,
RK_ObjCClassProperty,
RK_ObjCIvar,
@@ -266,6 +275,132 @@ private:
virtual void anchor();
};
struct CXXFieldRecord : APIRecord {
AccessControl Access;
CXXFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities, const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading, AccessControl Access,
bool IsFromSystemHeader)
: APIRecord(RK_CXXField, USR, Name, Loc, std::move(Availabilities),
LinkageInfo::none(), Comment, Declaration, SubHeading,
IsFromSystemHeader),
Access(Access) {}
CXXFieldRecord(RecordKind Kind, StringRef USR, StringRef Name,
PresumedLoc Loc, AvailabilitySet Availabilities,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, AccessControl Access,
bool IsFromSystemHeader)
: APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
LinkageInfo::none(), Comment, Declaration, SubHeading,
IsFromSystemHeader),
Access(Access) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_CXXField;
}
private:
virtual void anchor();
};
struct CXXMethodRecord : APIRecord {
FunctionSignature Signature;
AccessControl Access;
CXXMethodRecord() = delete;
CXXMethodRecord(RecordKind Kind, StringRef USR, StringRef Name,
PresumedLoc Loc, AvailabilitySet Availabilities,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature,
AccessControl Access, bool IsFromSystemHeader)
: APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
LinkageInfo::none(), Comment, Declaration, SubHeading,
IsFromSystemHeader),
Signature(Signature), Access(Access) {}
virtual ~CXXMethodRecord() = 0;
};
struct CXXConstructorRecord : CXXMethodRecord {
CXXConstructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading,
FunctionSignature Signature, AccessControl Access,
bool IsFromSystemHeader)
: CXXMethodRecord(RK_CXXConstructorMethod, USR, Name, Loc,
std::move(Availabilities), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_CXXConstructorMethod;
}
private:
virtual void anchor();
};
struct CXXDestructorRecord : CXXMethodRecord {
CXXDestructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities, const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading,
FunctionSignature Signature, AccessControl Access,
bool IsFromSystemHeader)
: CXXMethodRecord(RK_CXXDestructorMethod, USR, Name, Loc,
std::move(Availabilities), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_CXXDestructorMethod;
}
private:
virtual void anchor();
};
struct CXXStaticMethodRecord : CXXMethodRecord {
CXXStaticMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading,
FunctionSignature Signature, AccessControl Access,
bool IsFromSystemHeader)
: CXXMethodRecord(RK_CXXStaticMethod, USR, Name, Loc,
std::move(Availabilities), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_CXXStaticMethod;
}
private:
virtual void anchor();
};
struct CXXInstanceMethodRecord : CXXMethodRecord {
CXXInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading,
FunctionSignature Signature, AccessControl Access,
bool IsFromSystemHeader)
: CXXMethodRecord(RK_CXXInstanceMethod, USR, Name, Loc,
std::move(Availabilities), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_CXXInstanceMethod;
}
private:
virtual void anchor();
};
/// This holds information associated with Objective-C properties.
struct ObjCPropertyRecord : APIRecord {
/// The attributes associated with an Objective-C property.
@@ -444,6 +579,24 @@ struct SymbolReference {
bool empty() const { return Name.empty() && USR.empty() && Source.empty(); }
};
struct StaticFieldRecord : CXXFieldRecord {
SymbolReference Context;
StaticFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, SymbolReference Context,
AccessControl Access, bool IsFromSystemHeader)
: CXXFieldRecord(RK_StaticField, USR, Name, Loc,
std::move(Availabilities), Comment, Declaration,
SubHeading, Access, IsFromSystemHeader),
Context(Context) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_StaticField;
}
};
/// The base representation of an Objective-C container record. Holds common
/// information associated with Objective-C containers.
struct ObjCContainerRecord : APIRecord {
@@ -465,6 +618,28 @@ struct ObjCContainerRecord : APIRecord {
virtual ~ObjCContainerRecord() = 0;
};
struct CXXClassRecord : APIRecord {
SmallVector<std::unique_ptr<CXXFieldRecord>> Fields;
SmallVector<std::unique_ptr<CXXMethodRecord>> Methods;
SmallVector<SymbolReference> Bases;
CXXClassRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities, const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading, RecordKind Kind,
bool IsFromSystemHeader)
: APIRecord(Kind, USR, Name, Loc, std::move(Availabilities),
LinkageInfo::none(), Comment, Declaration, SubHeading,
IsFromSystemHeader) {}
static bool classof(const APIRecord *Record) {
return (Record->getKind() == RK_CXXClass);
}
private:
virtual void anchor();
};
/// This holds information associated with Objective-C categories.
struct ObjCCategoryRecord : ObjCContainerRecord {
SymbolReference Interface;
@@ -591,6 +766,12 @@ struct has_function_signature<ObjCInstanceMethodRecord>
: public std::true_type {};
template <>
struct has_function_signature<ObjCClassMethodRecord> : public std::true_type {};
template <>
struct has_function_signature<CXXMethodRecord> : public std::true_type {};
template <typename RecordTy> struct has_access : public std::false_type {};
template <> struct has_access<CXXMethodRecord> : public std::true_type {};
template <> struct has_access<CXXFieldRecord> : public std::true_type {};
/// APISet holds the set of API records collected from given inputs.
class APISet {
@@ -668,6 +849,41 @@ public:
DeclarationFragments SubHeading,
bool IsFromSystemHeader);
StaticFieldRecord *
addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, SymbolReference Context,
AccessControl Access, bool IsFromSystemHeaderg);
CXXFieldRecord *addCXXField(CXXClassRecord *CXXClass, StringRef Name,
StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading,
AccessControl Access, bool IsFromSystemHeader);
CXXClassRecord *
addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading,
APIRecord::RecordKind Kind, bool IsFromSystemHeader);
CXXMethodRecord *
addCXXMethod(CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
PresumedLoc Loc, AvailabilitySet Availability,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature,
bool IsStatic, AccessControl Access, bool IsFromSystemHeader);
CXXMethodRecord *addCXXSpecialMethod(
CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading,
FunctionSignature Signature, bool IsConstructor, AccessControl Access,
bool IsFromSystemHeader);
/// Create and add an Objective-C category record into the API set.
///
/// Note: the caller is responsible for keeping the StringRef \p Name and
@@ -790,8 +1006,12 @@ public:
const RecordMap<GlobalVariableRecord> &getGlobalVariables() const {
return GlobalVariables;
}
const RecordMap<StaticFieldRecord> &getStaticFields() const {
return StaticFields;
}
const RecordMap<EnumRecord> &getEnums() const { return Enums; }
const RecordMap<StructRecord> &getStructs() const { return Structs; }
const RecordMap<CXXClassRecord> &getCXXClasses() const { return CXXClasses; }
const RecordMap<ObjCCategoryRecord> &getObjCCategories() const {
return ObjCCategories;
}
@@ -845,8 +1065,10 @@ private:
llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
RecordMap<GlobalFunctionRecord> GlobalFunctions;
RecordMap<GlobalVariableRecord> GlobalVariables;
RecordMap<StaticFieldRecord> StaticFields;
RecordMap<EnumRecord> Enums;
RecordMap<StructRecord> Structs;
RecordMap<CXXClassRecord> CXXClasses;
RecordMap<ObjCCategoryRecord> ObjCCategories;
RecordMap<ObjCInterfaceRecord> ObjCInterfaces;
RecordMap<ObjCProtocolRecord> ObjCProtocols;

View File

@@ -173,10 +173,27 @@ public:
/// Get the corresponding FragmentKind from string \p S.
static FragmentKind parseFragmentKindFromString(StringRef S);
static DeclarationFragments
getExceptionSpecificationString(ExceptionSpecificationType ExceptionSpec);
static DeclarationFragments getStructureTypeFragment(const RecordDecl *Decl);
private:
std::vector<Fragment> Fragments;
};
class AccessControl {
public:
AccessControl(std::string Access) : Access(Access) {}
const std::string &getAccess() const { return Access; }
bool empty() const { return Access.empty(); }
private:
std::string Access;
};
/// Store function signature information with DeclarationFragments of the
/// return type and parameters.
class FunctionSignature {
@@ -219,6 +236,29 @@ private:
/// A factory class to build DeclarationFragments for different kinds of Decl.
class DeclarationFragmentsBuilder {
public:
/// Build FunctionSignature for a function-like declaration \c FunctionT like
/// FunctionDecl, ObjCMethodDecl, or CXXMethodDecl.
///
/// The logic and implementation of building a signature for a FunctionDecl,
/// CXXMethodDecl, and ObjCMethodDecl are exactly the same, but they do not
/// share a common base. This template helps reuse the code.
template <typename FunctionT>
static FunctionSignature getFunctionSignature(const FunctionT *Function);
static AccessControl getAccessControl(const Decl *Decl) {
switch (Decl->getAccess()) {
case AS_public:
return AccessControl("public");
case AS_private:
return AccessControl("private");
case AS_protected:
return AccessControl("protected");
case AS_none:
return AccessControl("none");
}
llvm_unreachable("Unhandled access control");
}
/// Build DeclarationFragments for a variable declaration VarDecl.
static DeclarationFragments getFragmentsForVar(const VarDecl *);
@@ -239,6 +279,19 @@ public:
/// Build DeclarationFragments for a struct record declaration RecordDecl.
static DeclarationFragments getFragmentsForStruct(const RecordDecl *);
static DeclarationFragments getFragmentsForCXXClass(const CXXRecordDecl *);
static DeclarationFragments
getFragmentsForSpecialCXXMethod(const CXXMethodDecl *);
static DeclarationFragments getFragmentsForCXXMethod(const CXXMethodDecl *);
static DeclarationFragments
getFragmentsForConversionFunction(const CXXConversionDecl *);
static DeclarationFragments
getFragmentsForOverloadedOperator(const CXXMethodDecl *);
/// Build DeclarationFragments for an Objective-C category declaration
/// ObjCCategoryDecl.
static DeclarationFragments
@@ -283,15 +336,6 @@ public:
/// Build a sub-heading for macro \p Name.
static DeclarationFragments getSubHeadingForMacro(StringRef Name);
/// Build FunctionSignature for a function-like declaration \c FunctionT like
/// FunctionDecl or ObjCMethodDecl.
///
/// The logic and implementation of building a signature for a FunctionDecl
/// and an ObjCMethodDecl are exactly the same, but they do not share a common
/// base. This template helps reuse the code.
template <typename FunctionT>
static FunctionSignature getFunctionSignature(const FunctionT *);
private:
DeclarationFragmentsBuilder() = delete;
@@ -316,6 +360,24 @@ private:
static DeclarationFragments getFragmentsForParam(const ParmVarDecl *);
};
template <typename FunctionT>
FunctionSignature
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
FunctionSignature Signature;
DeclarationFragments ReturnType, After;
ReturnType
.append(getFragmentsForType(Function->getReturnType(),
Function->getASTContext(), After))
.append(std::move(After));
Signature.setReturnType(ReturnType);
for (const auto *Param : Function->parameters())
Signature.addParameter(Param->getName(), getFragmentsForParam(Param));
return Signature;
}
} // namespace extractapi
} // namespace clang

View File

@@ -14,6 +14,10 @@
#ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
#define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Specifiers.h"
#include "clang/ExtractAPI/DeclarationFragments.h"
#include "llvm/ADT/FunctionExtras.h"
#include "clang/AST/ASTContext.h"
@@ -44,8 +48,14 @@ public:
bool VisitEnumDecl(const EnumDecl *Decl);
bool WalkUpFromRecordDecl(const RecordDecl *Decl);
bool WalkUpFromCXXRecordDecl(const CXXRecordDecl *Decl);
bool VisitRecordDecl(const RecordDecl *Decl);
bool VisitCXXRecordDecl(const CXXRecordDecl *Decl);
bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl);
bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl);
@@ -69,6 +79,20 @@ protected:
void recordStructFields(StructRecord *StructRecord,
const RecordDecl::field_range Fields);
/// Collect API information for the class fields and associate with the parent
/// struct
void recordCXXFields(CXXClassRecord *CXXClassRecord,
const RecordDecl::field_range Fields);
void recordCXXMethods(CXXClassRecord *CXXClassRecord,
const CXXRecordDecl::method_range Methods);
void recordConversionMethod(CXXClassRecord *CXXClassRecord,
const CXXMethodDecl *SpecialCXXMethod);
void recordSpecialCXXMethod(CXXClassRecord *CXXClassRecord,
const CXXMethodDecl *SpecialCXXMethod);
/// Collect API information for the Objective-C methods and associate with the
/// parent container.
void recordObjCMethods(ObjCContainerRecord *Container,
@@ -131,8 +155,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
if (isa<ParmVarDecl>(Decl))
return true;
// Skip non-global variables in records (struct/union/class).
if (Decl->getDeclContext()->isRecord())
// Skip non-global variables in records (struct/union/class) but not static
// members.
if (Decl->getDeclContext()->isRecord() && !Decl->isStaticDataMember())
return true;
// Skip local variables inside function or method.
@@ -165,9 +190,19 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);
// Add the global variable record to the API set.
API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
Declaration, SubHeading, isInSystemHeader(Decl));
if (Decl->isStaticDataMember()) {
SymbolReference Context;
auto Record = dyn_cast<RecordDecl>(Decl->getDeclContext());
Context.Name = Record->getName();
Context.USR = API.recordUSR(Record);
auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl);
API.addStaticField(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
Declaration, SubHeading, Context, Access,
isInSystemHeader(Decl));
} else
// Add the global variable record to the API set.
API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
Declaration, SubHeading, isInSystemHeader(Decl));
return true;
}
@@ -280,15 +315,23 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
// Skip C++ structs/classes/unions
// TODO: support C++ records
if (isa<CXXRecordDecl>(Decl))
return true;
bool ExtractAPIVisitorBase<Derived>::WalkUpFromRecordDecl(
const RecordDecl *Decl) {
getDerivedExtractAPIVisitor().VisitRecordDecl(Decl);
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::WalkUpFromCXXRecordDecl(
const CXXRecordDecl *Decl) {
getDerivedExtractAPIVisitor().VisitCXXRecordDecl(Decl);
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
return true;
// Collect symbol information.
StringRef Name = Decl->getName();
if (Name.empty())
@@ -322,6 +365,57 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
const CXXRecordDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
return true;
StringRef Name = Decl->getName();
StringRef USR = API.recordUSR(Decl);
PresumedLoc Loc =
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
DocComment Comment;
if (auto *RawComment =
getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
Context.getDiagnostics());
DeclarationFragments Declaration =
DeclarationFragmentsBuilder::getFragmentsForCXXClass(Decl);
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);
APIRecord::RecordKind Kind;
if (Decl->isUnion())
Kind = APIRecord::RecordKind::RK_Union;
else if (Decl->isStruct())
Kind = APIRecord::RecordKind::RK_Struct;
else
Kind = APIRecord::RecordKind::RK_CXXClass;
CXXClassRecord *CXXClassRecord =
API.addCXXClass(Name, USR, Loc, AvailabilitySet(Decl), Comment,
Declaration, SubHeading, Kind, isInSystemHeader(Decl));
// FIXME: store AccessSpecifier given by inheritance
for (const auto BaseSpecifier : Decl->bases()) {
// skip classes not inherited as public
if (BaseSpecifier.getAccessSpecifier() != AccessSpecifier::AS_public)
continue;
SymbolReference BaseClass;
CXXRecordDecl *BaseClassDecl =
BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl();
BaseClass.Name = BaseClassDecl->getName();
BaseClass.USR = API.recordUSR(BaseClassDecl);
CXXClassRecord->Bases.emplace_back(BaseClass);
}
getDerivedExtractAPIVisitor().recordCXXFields(CXXClassRecord, Decl->fields());
getDerivedExtractAPIVisitor().recordCXXMethods(CXXClassRecord,
Decl->methods());
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
const ObjCInterfaceDecl *Decl) {
@@ -558,6 +652,154 @@ void ExtractAPIVisitorBase<Derived>::recordStructFields(
}
}
template <typename Derived>
void ExtractAPIVisitorBase<Derived>::recordCXXFields(
CXXClassRecord *CXXClassRecord, const RecordDecl::field_range Fields) {
for (const auto *Field : Fields) {
// Collect symbol information.
StringRef Name = Field->getName();
StringRef USR = API.recordUSR(Field);
PresumedLoc Loc =
Context.getSourceManager().getPresumedLoc(Field->getLocation());
Context.getSourceManager().getPresumedLoc(Field->getLocation());
DocComment Comment;
if (auto *RawComment =
getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Field))
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
Context.getDiagnostics());
// Build declaration fragments and sub-heading for the struct field.
DeclarationFragments Declaration =
DeclarationFragmentsBuilder::getFragmentsForField(Field);
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Field);
AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Field);
API.addCXXField(CXXClassRecord, Name, USR, Loc, AvailabilitySet(Field),
Comment, Declaration, SubHeading, Access,
isInSystemHeader(Field));
}
}
/// Collect API information for constructors and destructors and associate with
/// the parent class.
template <typename Derived>
void ExtractAPIVisitorBase<Derived>::recordSpecialCXXMethod(
CXXClassRecord *CXXClassRecord, const CXXMethodDecl *CXXSpecialMethod) {
StringRef Name;
bool isConstructor = false;
if (isa<CXXConstructorDecl>(CXXSpecialMethod)) {
isConstructor = true;
Name = CXXClassRecord->Name;
} else if (isa<CXXDestructorDecl>(CXXSpecialMethod)) {
Name = API.copyString(CXXSpecialMethod->getNameAsString());
}
StringRef USR = API.recordUSR(CXXSpecialMethod);
PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(
CXXSpecialMethod->getLocation());
DocComment Comment;
if (auto *RawComment = getDerivedExtractAPIVisitor().fetchRawCommentForDecl(
CXXSpecialMethod))
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
Context.getDiagnostics());
// Build declaration fragments, sub-heading, and signature for the method.
DeclarationFragments Declaration =
DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod(
CXXSpecialMethod);
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(CXXSpecialMethod);
FunctionSignature Signature =
DeclarationFragmentsBuilder::getFunctionSignature(CXXSpecialMethod);
AccessControl Access =
DeclarationFragmentsBuilder::getAccessControl(CXXSpecialMethod);
API.addCXXSpecialMethod(CXXClassRecord, Name, USR, Loc,
AvailabilitySet(CXXSpecialMethod), Comment,
Declaration, SubHeading, Signature, isConstructor,
Access, isInSystemHeader(CXXSpecialMethod));
}
template <typename Derived>
void ExtractAPIVisitorBase<Derived>::recordConversionMethod(
CXXClassRecord *CXXClassRecord, const CXXMethodDecl *SpecialCXXMethod) {
StringRef Name = API.copyString(SpecialCXXMethod->getNameAsString());
StringRef USR = API.recordUSR(SpecialCXXMethod);
PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(
SpecialCXXMethod->getLocation());
DocComment Comment;
if (auto *RawComment = getDerivedExtractAPIVisitor().fetchRawCommentForDecl(
SpecialCXXMethod))
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
Context.getDiagnostics());
// Build declaration fragments, sub-heading, and signature for the method.
DeclarationFragments Declaration =
DeclarationFragmentsBuilder::getFragmentsForConversionFunction(
cast<CXXConversionDecl>(SpecialCXXMethod));
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(SpecialCXXMethod);
FunctionSignature Signature =
DeclarationFragmentsBuilder::getFunctionSignature(SpecialCXXMethod);
AccessControl Access =
DeclarationFragmentsBuilder::getAccessControl(SpecialCXXMethod);
API.addCXXMethod(CXXClassRecord, Name, USR, Loc,
AvailabilitySet(SpecialCXXMethod), Comment, Declaration,
SubHeading, Signature, SpecialCXXMethod->isStatic(), Access,
isInSystemHeader(SpecialCXXMethod));
}
template <typename Derived>
void ExtractAPIVisitorBase<Derived>::recordCXXMethods(
CXXClassRecord *CXXClassRecord, const CXXRecordDecl::method_range Methods) {
for (const auto *Method : Methods) {
if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) {
recordSpecialCXXMethod(CXXClassRecord, Method);
continue;
}
if (isa<CXXConversionDecl>(Method)) {
recordConversionMethod(CXXClassRecord, Method);
continue;
}
StringRef Name;
DeclarationFragments Declaration;
if (Method->isOverloadedOperator()) {
Name = API.copyString(Method->getNameAsString());
Declaration =
DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
Method);
} else {
Name = API.copyString(Method->getNameAsString());
Declaration =
DeclarationFragmentsBuilder::getFragmentsForCXXMethod(Method);
}
StringRef USR = API.recordUSR(Method);
PresumedLoc Loc =
Context.getSourceManager().getPresumedLoc(Method->getLocation());
DocComment Comment;
if (auto *RawComment =
getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Method))
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
Context.getDiagnostics());
// Build declaration fragments, sub-heading, and signature for the method.
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Method);
FunctionSignature Signature =
DeclarationFragmentsBuilder::getFunctionSignature(Method);
AccessControl Access =
DeclarationFragmentsBuilder::getAccessControl(Method);
API.addCXXMethod(CXXClassRecord, Name, USR, Loc, AvailabilitySet(Method),
Comment, Declaration, SubHeading, Signature,
Method->isStatic(), Access, isInSystemHeader(Method));
}
}
/// Collect API information for the Objective-C methods and associate with the
/// parent container.
template <typename Derived>

View File

@@ -29,6 +29,10 @@ public:
getDerived()->traverseEnumRecords();
getDerived()->traverseStaticFieldRecords();
getDerived()->traverseCXXClassRecords();
getDerived()->traverseStructRecords();
getDerived()->traverseObjCInterfaces();
@@ -60,6 +64,16 @@ public:
getDerived()->visitStructRecord(*Struct.second);
}
void traverseStaticFieldRecords() {
for (const auto &StaticField : API.getStaticFields())
getDerived()->visitStaticFieldRecord(*StaticField.second);
}
void traverseCXXClassRecords() {
for (const auto &Class : API.getCXXClasses())
getDerived()->visitCXXClassRecord(*Class.second);
}
void traverseObjCInterfaces() {
for (const auto &Interface : API.getObjCInterfaces())
getDerived()->visitObjCContainerRecord(*Interface.second);
@@ -92,6 +106,10 @@ public:
/// Visit a struct record.
void visitStructRecord(const StructRecord &Record){};
void visitStaticFieldRecord(const StaticFieldRecord &Record){};
void visitCXXClassRecord(const CXXClassRecord &Record){};
/// Visit an Objective-C container record.
void visitObjCContainerRecord(const ObjCContainerRecord &Record){};

View File

@@ -160,6 +160,10 @@ public:
/// Visit a struct record.
void visitStructRecord(const StructRecord &Record);
void visitStaticFieldRecord(const StaticFieldRecord &Record);
void visitCXXClassRecord(const CXXClassRecord &Record);
/// Visit an Objective-C container record.
void visitObjCContainerRecord(const ObjCContainerRecord &Record);

View File

@@ -120,6 +120,91 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc,
SubHeading, IsFromSystemHeader);
}
StaticFieldRecord *
APISet::addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities, LinkageInfo Linkage,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading, SymbolReference Context,
AccessControl Access, bool IsFromSystemHeader) {
return addTopLevelRecord(USRBasedLookupTable, StaticFields, USR, Name, Loc,
std::move(Availabilities), Linkage, Comment,
Declaration, SubHeading, Context, Access,
IsFromSystemHeader);
}
CXXFieldRecord *
APISet::addCXXField(CXXClassRecord *CXXClass, StringRef Name, StringRef USR,
PresumedLoc Loc, AvailabilitySet Availabilities,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, AccessControl Access,
bool IsFromSystemHeader) {
auto Record = std::make_unique<CXXFieldRecord>(
USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
SubHeading, Access, IsFromSystemHeader);
Record->ParentInformation = APIRecord::HierarchyInformation(
CXXClass->USR, CXXClass->Name, CXXClass->getKind(), CXXClass);
USRBasedLookupTable.insert({USR, Record.get()});
return CXXClass->Fields.emplace_back(std::move(Record)).get();
}
CXXClassRecord *
APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities, const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading, APIRecord::RecordKind Kind,
bool IsFromSystemHeader) {
return addTopLevelRecord(USRBasedLookupTable, CXXClasses, USR, Name, Loc,
std::move(Availabilities), Comment, Declaration,
SubHeading, Kind, IsFromSystemHeader);
}
CXXMethodRecord *APISet::addCXXMethod(
CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading,
FunctionSignature Signature, bool IsStatic, AccessControl Access,
bool IsFromSystemHeader) {
std::unique_ptr<CXXMethodRecord> Record;
if (IsStatic)
Record = std::make_unique<CXXStaticMethodRecord>(
USR, Name, Loc, std::move(Availability), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader);
else
Record = std::make_unique<CXXInstanceMethodRecord>(
USR, Name, Loc, std::move(Availability), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader);
Record->ParentInformation = APIRecord::HierarchyInformation(
CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(),
CXXClassRecord);
USRBasedLookupTable.insert({USR, Record.get()});
return CXXClassRecord->Methods.emplace_back(std::move(Record)).get();
}
CXXMethodRecord *APISet::addCXXSpecialMethod(
CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading,
FunctionSignature Signature, bool IsConstructor, AccessControl Access,
bool IsFromSystemHeader) {
std::unique_ptr<CXXMethodRecord> Record;
if (IsConstructor)
Record = std::make_unique<CXXConstructorRecord>(
USR, Name, Loc, std::move(Availability), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader);
else
Record = std::make_unique<CXXDestructorRecord>(
USR, Name, Loc, std::move(Availability), Comment, Declaration,
SubHeading, Signature, Access, IsFromSystemHeader);
Record->ParentInformation = APIRecord::HierarchyInformation(
CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(),
CXXClassRecord);
USRBasedLookupTable.insert({USR, Record.get()});
return CXXClassRecord->Methods.emplace_back(std::move(Record)).get();
}
ObjCCategoryRecord *APISet::addObjCCategory(
StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities, const DocComment &Comment,
@@ -282,6 +367,7 @@ APIRecord::~APIRecord() {}
ObjCContainerRecord::~ObjCContainerRecord() {}
ObjCMethodRecord::~ObjCMethodRecord() {}
ObjCPropertyRecord::~ObjCPropertyRecord() {}
CXXMethodRecord::~CXXMethodRecord() {}
void GlobalFunctionRecord::anchor() {}
void GlobalVariableRecord::anchor() {}
@@ -289,6 +375,12 @@ void EnumConstantRecord::anchor() {}
void EnumRecord::anchor() {}
void StructFieldRecord::anchor() {}
void StructRecord::anchor() {}
void CXXFieldRecord::anchor() {}
void CXXClassRecord::anchor() {}
void CXXConstructorRecord::anchor() {}
void CXXDestructorRecord::anchor() {}
void CXXInstanceMethodRecord::anchor() {}
void CXXStaticMethodRecord::anchor() {}
void ObjCInstancePropertyRecord::anchor() {}
void ObjCClassPropertyRecord::anchor() {}
void ObjCInstanceVariableRecord::anchor() {}

View File

@@ -12,8 +12,12 @@
//===----------------------------------------------------------------------===//
#include "clang/ExtractAPI/DeclarationFragments.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Parse/Parser.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang::extractapi;
@@ -84,6 +88,59 @@ DeclarationFragments::parseFragmentKindFromString(StringRef S) {
.Default(DeclarationFragments::FragmentKind::None);
}
DeclarationFragments DeclarationFragments::getExceptionSpecificationString(
ExceptionSpecificationType ExceptionSpec) {
DeclarationFragments Fragments;
switch (ExceptionSpec) {
case ExceptionSpecificationType::EST_None:
return Fragments;
case ExceptionSpecificationType::EST_DynamicNone:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("throw", DeclarationFragments::FragmentKind::Keyword)
.append("(", DeclarationFragments::FragmentKind::Text)
.append(")", DeclarationFragments::FragmentKind::Text);
case ExceptionSpecificationType::EST_Dynamic:
// FIXME: throw(int), get types of inner expression
return Fragments;
case ExceptionSpecificationType::EST_BasicNoexcept:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("noexcept", DeclarationFragments::FragmentKind::Keyword);
case ExceptionSpecificationType::EST_DependentNoexcept:
// FIXME: throw(conditional-expression), get expression
break;
case ExceptionSpecificationType::EST_NoexceptFalse:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("noexcept", DeclarationFragments::FragmentKind::Keyword)
.append("(", DeclarationFragments::FragmentKind::Text)
.append("false", DeclarationFragments::FragmentKind::Keyword)
.append(")", DeclarationFragments::FragmentKind::Text);
case ExceptionSpecificationType::EST_NoexceptTrue:
return Fragments.append(" ", DeclarationFragments::FragmentKind::Text)
.append("noexcept", DeclarationFragments::FragmentKind::Keyword)
.append("(", DeclarationFragments::FragmentKind::Text)
.append("true", DeclarationFragments::FragmentKind::Keyword)
.append(")", DeclarationFragments::FragmentKind::Text);
default:
return Fragments;
}
llvm_unreachable("Unhandled exception specification");
}
DeclarationFragments
DeclarationFragments::getStructureTypeFragment(const RecordDecl *Record) {
DeclarationFragments Fragments;
if (Record->isStruct())
Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);
else if (Record->isUnion())
Fragments.append("union", DeclarationFragments::FragmentKind::Keyword);
else
Fragments.append("class", DeclarationFragments::FragmentKind::Keyword);
return Fragments;
}
// NNS stores C++ nested name specifiers, which are prefixes to qualified names.
// Build declaration fragments for NNS recursively so that we have the USR for
// every part in a qualified name, and also leaves the actual underlying type
@@ -369,6 +426,9 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
DeclarationFragments Fragments;
if (Var->isConstexpr())
Fragments.append("constexpr ", DeclarationFragments::FragmentKind::Keyword);
StorageClass SC = Var->getStorageClass();
if (SC != SC_None)
Fragments
@@ -438,7 +498,10 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
case SC_Register:
llvm_unreachable("invalid for functions");
}
// FIXME: Handle C++ function specifiers: constexpr, consteval, explicit, etc.
if (Func->isConsteval()) // if consteval, it is also constexpr
Fragments.append("consteval ", DeclarationFragments::FragmentKind::Keyword);
else if (Func->isConstexpr())
Fragments.append("constexpr ", DeclarationFragments::FragmentKind::Keyword);
// FIXME: Is `after` actually needed here?
DeclarationFragments After;
@@ -457,6 +520,9 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Func->getExceptionSpecType()));
// FIXME: Handle exception specifiers: throw, noexcept
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
@@ -493,7 +559,13 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) {
DeclarationFragments After;
return getFragmentsForType(Field->getType(), Field->getASTContext(), After)
DeclarationFragments Fragments;
if (Field->isMutable())
Fragments.append("mutable", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
return Fragments
.append(
getFragmentsForType(Field->getType(), Field->getASTContext(), After))
.appendSpace()
.append(Field->getName(), DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After))
@@ -515,6 +587,156 @@ DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) {
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass(
const CXXRecordDecl *Record) {
if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl())
return getFragmentsForTypedef(TypedefNameDecl);
DeclarationFragments Fragments;
Fragments.append(DeclarationFragments::getStructureTypeFragment(Record));
if (!Record->getName().empty())
Fragments.appendSpace().append(
Record->getName(), DeclarationFragments::FragmentKind::Identifier);
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod(
const CXXMethodDecl *Method) {
DeclarationFragments Fragments;
StringRef Name;
if (isa<CXXConstructorDecl>(Method)) {
auto *Constructor = dyn_cast<CXXConstructorDecl>(Method);
Name = cast<CXXRecordDecl>(Constructor->getDeclContext())->getName();
if (Constructor->isExplicit())
Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
} else if (isa<CXXDestructorDecl>(Method))
Name = StringRef(Method->getNameAsString());
DeclarationFragments After;
Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod(
const CXXMethodDecl *Method) {
DeclarationFragments Fragments;
StringRef Name;
Name = Method->getName();
if (Method->isStatic())
Fragments.append("static", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
if (Method->isConstexpr())
Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
if (Method->isVolatile())
Fragments.append("volatile", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
// Build return type
DeclarationFragments After;
Fragments
.append(getFragmentsForType(Method->getReturnType(),
Method->getASTContext(), After))
.appendSpace()
.append(Name, DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
if (Method->isConst())
Fragments.appendSpace().append("const",
DeclarationFragments::FragmentKind::Keyword);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForConversionFunction(
const CXXConversionDecl *ConversionFunction) {
DeclarationFragments Fragments;
if (ConversionFunction->isExplicit())
Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
Fragments.append("operator", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
Fragments
.append(ConversionFunction->getConversionType().getAsString(),
DeclarationFragments::FragmentKind::TypeIdentifier)
.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = ConversionFunction->getNumParams(); i != end;
++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(ConversionFunction->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
if (ConversionFunction->isConst())
Fragments.appendSpace().append("const",
DeclarationFragments::FragmentKind::Keyword);
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
const CXXMethodDecl *Method) {
DeclarationFragments Fragments;
// Build return type
DeclarationFragments After;
Fragments
.append(getFragmentsForType(Method->getReturnType(),
Method->getASTContext(), After))
.appendSpace()
.append(Method->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(getFragmentsForParam(Method->getParamDecl(i)));
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
if (Method->isConst())
Fragments.appendSpace().append("const",
DeclarationFragments::FragmentKind::Keyword);
Fragments.append(DeclarationFragments::getExceptionSpecificationString(
Method->getExceptionSpecType()));
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
const MacroDirective *MD) {
@@ -765,24 +987,6 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef(
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
template <typename FunctionT>
FunctionSignature
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
FunctionSignature Signature;
DeclarationFragments ReturnType, After;
ReturnType
.append(getFragmentsForType(Function->getReturnType(),
Function->getASTContext(), After))
.append(std::move(After));
Signature.setReturnType(ReturnType);
for (const auto *Param : Function->parameters())
Signature.addParameter(Param->getName(), getFragmentsForParam(Param));
return Signature;
}
// Instantiate template for FunctionDecl.
template FunctionSignature
DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *);
@@ -795,7 +999,18 @@ DeclarationFragmentsBuilder::getFunctionSignature(const ObjCMethodDecl *);
DeclarationFragments
DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) {
DeclarationFragments Fragments;
if (!Decl->getName().empty())
if (isa<CXXConstructorDecl>(Decl) || isa<CXXDestructorDecl>(Decl))
Fragments.append(cast<CXXRecordDecl>(Decl->getDeclContext())->getName(),
DeclarationFragments::FragmentKind::Identifier);
else if (isa<CXXConversionDecl>(Decl)) {
Fragments.append(
cast<CXXConversionDecl>(Decl)->getConversionType().getAsString(),
DeclarationFragments::FragmentKind::Identifier);
} else if (isa<CXXMethodDecl>(Decl) &&
cast<CXXMethodDecl>(Decl)->isOverloadedOperator()) {
Fragments.append(Decl->getNameAsString(),
DeclarationFragments::FragmentKind::Identifier);
} else if (!Decl->getName().empty())
Fragments.append(Decl->getName(),
DeclarationFragments::FragmentKind::Identifier);
return Fragments;

View File

@@ -38,6 +38,14 @@ void serializeObject(Object &Paren, StringRef Key, std::optional<Object> Obj) {
Paren[Key] = std::move(*Obj);
}
/// Helper function to inject a StringRef \p String into an object \p Paren at
/// position \p Key
void serializeString(Object &Paren, StringRef Key,
std::optional<std::string> String) {
if (String)
Paren[Key] = std::move(*String);
}
/// Helper function to inject a JSON array \p Array into object \p Paren at
/// position \p Key.
void serializeArray(Object &Paren, StringRef Key, std::optional<Array> Array) {
@@ -189,9 +197,10 @@ StringRef getLanguageName(Language Lang) {
return "c";
case Language::ObjC:
return "objective-c";
case Language::CXX:
return "c++";
// Unsupported language currently
case Language::CXX:
case Language::ObjCXX:
case Language::OpenCL:
case Language::OpenCLCXX:
@@ -366,6 +375,38 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
Kind["identifier"] = AddLangPrefix("struct");
Kind["displayName"] = "Structure";
break;
case APIRecord::RK_CXXField:
Kind["identifier"] = AddLangPrefix("property");
Kind["displayName"] = "Instance Property";
break;
case APIRecord::RK_Union:
Kind["identifier"] = AddLangPrefix("union");
Kind["displayName"] = "Union";
break;
case APIRecord::RK_StaticField:
Kind["identifier"] = AddLangPrefix("type.property");
Kind["displayName"] = "Type Property";
break;
case APIRecord::RK_CXXClass:
Kind["identifier"] = AddLangPrefix("class");
Kind["displayName"] = "Class";
break;
case APIRecord::RK_CXXStaticMethod:
Kind["identifier"] = AddLangPrefix("type.method");
Kind["displayName"] = "Static Method";
break;
case APIRecord::RK_CXXInstanceMethod:
Kind["identifier"] = AddLangPrefix("method");
Kind["displayName"] = "Instance Method";
break;
case APIRecord::RK_CXXConstructorMethod:
Kind["identifier"] = AddLangPrefix("method");
Kind["displayName"] = "Constructor";
break;
case APIRecord::RK_CXXDestructorMethod:
Kind["identifier"] = AddLangPrefix("method");
Kind["displayName"] = "Destructor";
break;
case APIRecord::RK_ObjCIvar:
Kind["identifier"] = AddLangPrefix("ivar");
Kind["displayName"] = "Instance Variable";
@@ -470,6 +511,31 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) {
Record, has_function_signature<RecordTy>()));
}
template <typename RecordTy>
std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
std::true_type) {
const auto &AccessControl = Record.Access;
std::string Access;
if (AccessControl.empty())
return std::nullopt;
Access = AccessControl.getAccess();
return Access;
}
template <typename RecordTy>
std::optional<std::string> serializeAccessMixinImpl(const RecordTy &Record,
std::false_type) {
return std::nullopt;
}
template <typename RecordTy>
void serializeAccessMixin(Object &Paren, const RecordTy &Record) {
auto accessLevel = serializeAccessMixinImpl(Record, has_access<RecordTy>());
if (!accessLevel.has_value())
accessLevel = "public";
serializeString(Paren, "accessLevel", accessLevel);
}
struct PathComponent {
StringRef USR;
StringRef Name;
@@ -543,7 +609,6 @@ Array generateParentContexts(const RecordTy &Record, const APISet &API,
return ParentContexts;
}
} // namespace
/// Defines the format version emitted by SymbolGraphSerializer.
@@ -602,9 +667,6 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
serializeObject(Obj, "docComment", serializeDocComment(Record.Comment));
serializeArray(Obj, "declarationFragments",
serializeDeclarationFragments(Record.Declaration));
// TODO: Once we keep track of symbol access information serialize it
// correctly here.
Obj["accessLevel"] = "public";
SmallVector<StringRef, 4> PathComponentsNames;
// If this returns true it indicates that we couldn't find a symbol in the
// hierarchy.
@@ -617,6 +679,7 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
serializeArray(Obj, "pathComponents", Array(PathComponentsNames));
serializeFunctionSignatureMixin(Obj, Record);
serializeAccessMixin(Obj, Record);
return Obj;
}
@@ -698,6 +761,28 @@ void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) {
serializeMembers(Record, Record.Fields);
}
void SymbolGraphSerializer::visitStaticFieldRecord(
const StaticFieldRecord &Record) {
auto StaticField = serializeAPIRecord(Record);
if (!StaticField)
return;
Symbols.emplace_back(std::move(*StaticField));
serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context);
}
void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) {
auto Class = serializeAPIRecord(Record);
if (!Class)
return;
Symbols.emplace_back(std::move(*Class));
serializeMembers(Record, Record.Fields);
serializeMembers(Record, Record.Methods);
for (const auto Base : Record.Bases)
serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
}
void SymbolGraphSerializer::visitObjCContainerRecord(
const ObjCContainerRecord &Record) {
auto ObjCContainer = serializeAPIRecord(Record);
@@ -761,6 +846,12 @@ void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) {
case APIRecord::RK_Struct:
visitStructRecord(*cast<StructRecord>(Record));
break;
case APIRecord::RK_StaticField:
visitStaticFieldRecord(*cast<StaticFieldRecord>(Record));
break;
case APIRecord::RK_CXXClass:
visitCXXClassRecord(*cast<CXXClassRecord>(Record));
break;
case APIRecord::RK_ObjCInterface:
visitObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
break;

View File

@@ -0,0 +1,366 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
class Foo {
private:
int a;
mutable int b;
protected:
int c;
public:
int d;
};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "memberOf",
"source": "c:@S@Foo@FI@a",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@FI@b",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@FI@c",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@FI@d",
"target": "c:@S@Foo",
"targetFallback": "Foo"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "a"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@FI@a"
},
"kind": {
"displayName": "Instance Property",
"identifier": "c++.property"
},
"location": {
"position": {
"character": 7,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "a"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "a"
}
],
"title": "a"
},
"pathComponents": [
"Foo",
"a"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "mutable"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "b"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@FI@b"
},
"kind": {
"displayName": "Instance Property",
"identifier": "c++.property"
},
"location": {
"position": {
"character": 15,
"line": 4
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "b"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "b"
}
],
"title": "b"
},
"pathComponents": [
"Foo",
"b"
]
},
{
"accessLevel": "protected",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "c"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@FI@c"
},
"kind": {
"displayName": "Instance Property",
"identifier": "c++.property"
},
"location": {
"position": {
"character": 7,
"line": 7
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "c"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "c"
}
],
"title": "c"
},
"pathComponents": [
"Foo",
"c"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "d"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@FI@d"
},
"kind": {
"displayName": "Instance Property",
"identifier": "c++.property"
},
"location": {
"position": {
"character": 7,
"line": 10
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "d"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "d"
}
],
"title": "d"
},
"pathComponents": [
"Foo",
"d"
]
}
]
}

View File

@@ -0,0 +1,227 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
class Foo {
Foo();
~Foo();
};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "memberOf",
"source": "c:@S@Foo@F@Foo#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@F@~Foo#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": "();"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@Foo#"
},
"kind": {
"displayName": "Constructor",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 3,
"line": 2
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo",
"Foo"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "identifier",
"spelling": "~Foo"
},
{
"kind": "text",
"spelling": "();"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@~Foo#"
},
"kind": {
"displayName": "Destructor",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 3,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "~Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "~Foo"
},
"pathComponents": [
"Foo",
"~Foo"
]
}
]
}

View File

@@ -0,0 +1,251 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
class Foo {
operator int();
explicit operator double();
};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "memberOf",
"source": "c:@S@Foo@F@operator int#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@F@operator double#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "operator"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"spelling": "int"
},
{
"kind": "text",
"spelling": "();"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@operator int#"
},
"kind": {
"displayName": "Instance Method",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 3,
"line": 2
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "operator int"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "int"
}
],
"title": "operator int"
},
"pathComponents": [
"Foo",
"operator int"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "explicit"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "keyword",
"spelling": "operator"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"spelling": "double"
},
{
"kind": "text",
"spelling": "();"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:d",
"spelling": "double"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@operator double#"
},
"kind": {
"displayName": "Instance Method",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 12,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "operator double"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "double"
}
],
"title": "operator double"
},
"pathComponents": [
"Foo",
"operator double"
]
}
]
}

View File

@@ -0,0 +1,293 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
void getFoo() noexcept;
void getBar() noexcept(true);
void getFooBar() noexcept(false);
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "getFoo"
},
{
"kind": "text",
"spelling": "()"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "keyword",
"spelling": "noexcept"
},
{
"kind": "text",
"spelling": ";"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@F@getFoo#"
},
"kind": {
"displayName": "Function",
"identifier": "c++.func"
},
"location": {
"position": {
"character": 6,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "getFoo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "getFoo"
}
],
"title": "getFoo"
},
"pathComponents": [
"getFoo"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "getBar"
},
{
"kind": "text",
"spelling": "()"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "keyword",
"spelling": "noexcept"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "keyword",
"spelling": "true"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@F@getBar#"
},
"kind": {
"displayName": "Function",
"identifier": "c++.func"
},
"location": {
"position": {
"character": 6,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "getBar"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "getBar"
}
],
"title": "getBar"
},
"pathComponents": [
"getBar"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "getFooBar"
},
{
"kind": "text",
"spelling": "()"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "keyword",
"spelling": "noexcept"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "keyword",
"spelling": "false"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@F@getFooBar#"
},
"kind": {
"displayName": "Function",
"identifier": "c++.func"
},
"location": {
"position": {
"character": 6,
"line": 5
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "getFooBar"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "getFooBar"
}
],
"title": "getFooBar"
},
"pathComponents": [
"getFooBar"
]
}
]
}

View File

@@ -0,0 +1,467 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
class Foo {
int getCount();
void setLength(int length) noexcept;
public:
static double getFoo();
protected:
constexpr int getBar() const;
};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "memberOf",
"source": "c:@S@Foo@F@getCount#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@F@setLength#I#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@F@getFoo#S",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@F@getBar#1",
"target": "c:@S@Foo",
"targetFallback": "Foo"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "getCount"
},
{
"kind": "text",
"spelling": "();"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@getCount#"
},
"kind": {
"displayName": "Instance Method",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 7,
"line": 2
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "getCount"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "getCount"
}
],
"title": "getCount"
},
"pathComponents": [
"Foo",
"getCount"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "setLength"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "length"
},
{
"kind": "text",
"spelling": ")"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "keyword",
"spelling": "noexcept"
},
{
"kind": "text",
"spelling": ";"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "length"
}
],
"name": "length"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@setLength#I#"
},
"kind": {
"displayName": "Instance Method",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 8,
"line": 4
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "setLength"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "setLength"
}
],
"title": "setLength"
},
"pathComponents": [
"Foo",
"setLength"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "static"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:d",
"spelling": "double"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "getFoo"
},
{
"kind": "text",
"spelling": "();"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:d",
"spelling": "double"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@getFoo#S"
},
"kind": {
"displayName": "Static Method",
"identifier": "c++.type.method"
},
"location": {
"position": {
"character": 17,
"line": 7
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "getFoo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "getFoo"
}
],
"title": "getFoo"
},
"pathComponents": [
"Foo",
"getFoo"
]
},
{
"accessLevel": "protected",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "constexpr"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "getBar"
},
{
"kind": "text",
"spelling": "() "
},
{
"kind": "keyword",
"spelling": "const"
},
{
"kind": "text",
"spelling": ";"
}
],
"functionSignature": {
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@getBar#1"
},
"kind": {
"displayName": "Instance Method",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 17,
"line": 10
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "getBar"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "getBar"
}
],
"title": "getBar"
},
"pathComponents": [
"Foo",
"getBar"
]
}
]
}

View File

@@ -0,0 +1,293 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
class Fizz {};
class Foo : public Fizz {};
class Bar : public Fizz {};
class FooBar : public Foo, public Bar{};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "inheritsFrom",
"source": "c:@S@Foo",
"target": "c:@S@Fizz",
"targetFallback": "Fizz"
},
{
"kind": "inheritsFrom",
"source": "c:@S@Bar",
"target": "c:@S@Fizz",
"targetFallback": "Fizz"
},
{
"kind": "inheritsFrom",
"source": "c:@S@FooBar",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "inheritsFrom",
"source": "c:@S@FooBar",
"target": "c:@S@Bar",
"targetFallback": "Bar"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Fizz"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Fizz"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Fizz"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Fizz"
}
],
"title": "Fizz"
},
"pathComponents": [
"Fizz"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Bar"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Bar"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 5
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Bar"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Bar"
}
],
"title": "Bar"
},
"pathComponents": [
"Bar"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "FooBar"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@FooBar"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 7
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "FooBar"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "FooBar"
}
],
"title": "FooBar"
},
"pathComponents": [
"FooBar"
]
}
]
}

View File

@@ -0,0 +1,210 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
class Foo {
int operator+(int x);
};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "memberOf",
"source": "c:@S@Foo@F@operator+#I#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "private",
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "operator+"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "x"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "x"
}
],
"name": "x"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@F@operator+#I#"
},
"kind": {
"displayName": "Instance Method",
"identifier": "c++.method"
},
"location": {
"position": {
"character": 7,
"line": 2
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "operator+"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "operator+"
}
],
"title": "operator+"
},
"pathComponents": [
"Foo",
"operator+"
]
}
]
}

View File

@@ -0,0 +1,162 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \
// RUN: %t/input.h -o %t/output.json -Xclang -verify
// Generator version is not consistent across test runs, normalize it.
// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \
// RUN: %t/output.json >> %t/output-normalized.json
// RUN: diff %t/reference.output.json %t/output-normalized.json
//--- input.h
class Foo {};
class Bar : public Foo {};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "inheritsFrom",
"source": "c:@S@Bar",
"target": "c:@S@Foo",
"targetFallback": "Foo"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Bar"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Bar"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Bar"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Bar"
}
],
"title": "Bar"
},
"pathComponents": [
"Bar"
]
}
]
}