[clang] 75f55eb - [clang][ExtractAPI] Add support for C++ classes with fix
Erick Velez via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 4 07:31:45 PDT 2023
Author: Erick Velez
Date: 2023-08-04T07:19:24-07:00
New Revision: 75f55eb331e0e9d8edb60115739b1b4794188df8
URL: https://github.com/llvm/llvm-project/commit/75f55eb331e0e9d8edb60115739b1b4794188df8
DIFF: https://github.com/llvm/llvm-project/commit/75f55eb331e0e9d8edb60115739b1b4794188df8.diff
LOG: [clang][ExtractAPI] Add support for C++ classes with fix
Reintroduce D153557 with fix for use-after-free from f4de606ef271 and minor changes.
Reviewed By: dang
Differential Revision: https://reviews.llvm.org/D157007
Added:
clang/test/ExtractAPI/class.cpp
clang/test/ExtractAPI/constructor_destructor.cpp
clang/test/ExtractAPI/conversions.cpp
clang/test/ExtractAPI/function_noexcepts.cpp
clang/test/ExtractAPI/methods.cpp
clang/test/ExtractAPI/multiple_inheritance.cpp
clang/test/ExtractAPI/operator_overload.cpp
clang/test/ExtractAPI/simple_inheritance.cpp
Modified:
clang/include/clang/ExtractAPI/API.h
clang/include/clang/ExtractAPI/DeclarationFragments.h
clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
clang/lib/ExtractAPI/API.cpp
clang/lib/ExtractAPI/DeclarationFragments.cpp
clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
utils/bazel/llvm-project-overlay/clang/BUILD.bazel
Removed:
################################################################################
diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index a18879f39645f7..437ac953b7409d 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -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 @@ struct StructRecord : APIRecord {
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 @@ class APISet {
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 @@ class APISet {
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 @@ class APISet {
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;
diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index 82f0c42ab8aa09..dc8a02e8811aed 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -173,10 +173,27 @@ class DeclarationFragments {
/// 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 @@ class FunctionSignature {
/// A factory class to build DeclarationFragments for
diff erent 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 @@ class DeclarationFragmentsBuilder {
/// 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 @@ class DeclarationFragmentsBuilder {
/// 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 @@ class DeclarationFragmentsBuilder {
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
diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index f0882afb5a61bf..bc35ee73ca8ad5 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -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 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
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 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
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,155 @@ 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)) {
+ // Copy string to get name with '~'.
+ 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>
diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
index 006e92be29555c..afb84c20ddefea 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -29,6 +29,10 @@ template <typename Derived> class APISetVisitor {
getDerived()->traverseEnumRecords();
+ getDerived()->traverseStaticFieldRecords();
+
+ getDerived()->traverseCXXClassRecords();
+
getDerived()->traverseStructRecords();
getDerived()->traverseObjCInterfaces();
@@ -60,6 +64,16 @@ template <typename Derived> class APISetVisitor {
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 @@ template <typename Derived> class APISetVisitor {
/// 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){};
diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index e77903f8ba08ff..d975e7a61345da 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -160,6 +160,10 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
/// 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);
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 10e79b37de7395..02875325099360 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -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() {}
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 20335768ac30b0..703142a910764c 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -12,6 +12,9 @@
//===----------------------------------------------------------------------===//
#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 "llvm/ADT/StringSwitch.h"
@@ -84,6 +87,58 @@ 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 +424,10 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) {
DeclarationFragments Fragments;
+ if (Var->isConstexpr())
+ Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+
StorageClass SC = Var->getStorageClass();
if (SC != SC_None)
Fragments
@@ -438,7 +497,12 @@ 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)
+ .appendSpace();
+ else if (Func->isConstexpr())
+ Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
// FIXME: Is `after` actually needed here?
DeclarationFragments After;
@@ -457,7 +521,9 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
}
Fragments.append(")", DeclarationFragments::FragmentKind::Text);
- // FIXME: Handle exception specifiers: throw, noexcept
+ Fragments.append(DeclarationFragments::getExceptionSpecificationString(
+ Func->getExceptionSpecType()));
+
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,154 @@ 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;
+ std::string Name;
+ if (isa<CXXConstructorDecl>(Method)) {
+ Name = Method->getNameAsString();
+ if (dyn_cast<CXXConstructorDecl>(Method)->isExplicit())
+ Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace();
+ } else if (isa<CXXDestructorDecl>(Method))
+ Name = 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 = 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 +985,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 +997,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;
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 534e9288cc713b..c9fbf0d87ac218 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -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;
diff --git a/clang/test/ExtractAPI/class.cpp b/clang/test/ExtractAPI/class.cpp
new file mode 100644
index 00000000000000..4a88029aeb0554
--- /dev/null
+++ b/clang/test/ExtractAPI/class.cpp
@@ -0,0 +1,366 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at Foo@FI at a",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@FI at b",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@FI at c",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@FI at d",
+ "target": "c:@S at 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 at 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 at Foo@FI at 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 at Foo@FI at 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 at Foo@FI at 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 at Foo@FI at 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"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/constructor_destructor.cpp b/clang/test/ExtractAPI/constructor_destructor.cpp
new file mode 100644
index 00000000000000..63048d1a4baed6
--- /dev/null
+++ b/clang/test/ExtractAPI/constructor_destructor.cpp
@@ -0,0 +1,227 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at Foo@F at Foo#",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@F@~Foo#",
+ "target": "c:@S at 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 at 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 at Foo@F at 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 at 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"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/conversions.cpp b/clang/test/ExtractAPI/conversions.cpp
new file mode 100644
index 00000000000000..326c84877ca7da
--- /dev/null
+++ b/clang/test/ExtractAPI/conversions.cpp
@@ -0,0 +1,251 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at Foo@F at operator int#",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@F at operator double#",
+ "target": "c:@S at 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 at 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 at Foo@F at 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 at Foo@F at 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"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/function_noexcepts.cpp b/clang/test/ExtractAPI/function_noexcepts.cpp
new file mode 100644
index 00000000000000..0ce4dd2e43e510
--- /dev/null
+++ b/clang/test/ExtractAPI/function_noexcepts.cpp
@@ -0,0 +1,293 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at 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 at 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 at 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"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/methods.cpp b/clang/test/ExtractAPI/methods.cpp
new file mode 100644
index 00000000000000..aed21c6afba82e
--- /dev/null
+++ b/clang/test/ExtractAPI/methods.cpp
@@ -0,0 +1,467 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at Foo@F at getCount#",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@F at setLength#I#",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@F at getFoo#S",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@F at getBar#1",
+ "target": "c:@S at 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 at 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 at Foo@F at 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 at Foo@F at 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 at Foo@F at 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 at Foo@F at 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"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/multiple_inheritance.cpp b/clang/test/ExtractAPI/multiple_inheritance.cpp
new file mode 100644
index 00000000000000..505cfa3c44be68
--- /dev/null
+++ b/clang/test/ExtractAPI/multiple_inheritance.cpp
@@ -0,0 +1,293 @@
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at Foo",
+ "target": "c:@S at Fizz",
+ "targetFallback": "Fizz"
+ },
+ {
+ "kind": "inheritsFrom",
+ "source": "c:@S at Bar",
+ "target": "c:@S at Fizz",
+ "targetFallback": "Fizz"
+ },
+ {
+ "kind": "inheritsFrom",
+ "source": "c:@S at FooBar",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "inheritsFrom",
+ "source": "c:@S at FooBar",
+ "target": "c:@S at 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 at 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 at 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 at 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 at 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"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/operator_overload.cpp b/clang/test/ExtractAPI/operator_overload.cpp
new file mode 100644
index 00000000000000..084038e42f59f7
--- /dev/null
+++ b/clang/test/ExtractAPI/operator_overload.cpp
@@ -0,0 +1,210 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at Foo@F at operator+#I#",
+ "target": "c:@S at 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 at 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 at Foo@F at 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+"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/simple_inheritance.cpp b/clang/test/ExtractAPI/simple_inheritance.cpp
new file mode 100644
index 00000000000000..4152d6c2859ea0
--- /dev/null
+++ b/clang/test/ExtractAPI/simple_inheritance.cpp
@@ -0,0 +1,162 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s at INPUT_DIR@%{/t:regex_replacement}@g" \
+// RUN: %t/reference.output.json.in >> %t/reference.output.json
+// RUN: %clang_cc1 -extract-api -triple arm64-apple-macosx \
+// RUN: -x c++-header %t/input.h -o %t/output.json -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 at Bar",
+ "target": "c:@S at 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 at 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 at 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"
+ ]
+ }
+ ]
+}
diff --git a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
index 6b8ac17ef874cf..037719a51dd1e8 100644
--- a/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/clang/BUILD.bazel
@@ -2509,7 +2509,6 @@ cc_library(
":frontend",
":index",
":lex",
- ":parse",
"//llvm:Option",
"//llvm:Support",
"//llvm:TargetParser",
More information about the cfe-commits
mailing list