[clang] 7ba37f4 - [clang][ExtractAPI] Add support for C++ class templates and concepts
Erick Velez via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 18 13:41:17 PDT 2023
Author: Erick Velez
Date: 2023-08-18T13:40:22-07:00
New Revision: 7ba37f4e46a5bbb1dc42f1ea1722296ea32034d5
URL: https://github.com/llvm/llvm-project/commit/7ba37f4e46a5bbb1dc42f1ea1722296ea32034d5
DIFF: https://github.com/llvm/llvm-project/commit/7ba37f4e46a5bbb1dc42f1ea1722296ea32034d5.diff
LOG: [clang][ExtractAPI] Add support for C++ class templates and concepts
Add has_template template, DeclarationFragmentBuilder functions, and tests for class templates, specializations/partial specs, and concepts.
Depends on D157007
Reviewed By: dang
Differential Revision: https://reviews.llvm.org/D157076
Added:
clang/test/ExtractAPI/class_template.cpp
clang/test/ExtractAPI/class_template_param_inheritance.cpp
clang/test/ExtractAPI/class_template_partial_spec.cpp
clang/test/ExtractAPI/class_template_spec.cpp
clang/test/ExtractAPI/concept.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
Removed:
################################################################################
diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index a965f49c8e91b2..83ff982be559b9 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -36,6 +36,86 @@
namespace clang {
namespace extractapi {
+class Template {
+ struct TemplateParameter {
+ // "class", "typename", or concept name
+ std::string Type;
+ std::string Name;
+ unsigned int Index;
+ unsigned int Depth;
+ bool IsParameterPack;
+
+ TemplateParameter(std::string Type, std::string Name, unsigned int Index,
+ unsigned int Depth, bool IsParameterPack)
+ : Type(Type), Name(Name), Index(Index), Depth(Depth),
+ IsParameterPack(IsParameterPack) {}
+ };
+
+ struct TemplateConstraint {
+ // type name of the constraint, if it has one
+ std::string Type;
+ std::string Kind;
+ std::string LHS, RHS;
+ };
+ llvm::SmallVector<TemplateParameter> Parameters;
+ llvm::SmallVector<TemplateConstraint> Constraints;
+
+public:
+ Template() = default;
+
+ Template(const TemplateDecl *Decl) {
+ for (auto *const Parameter : *Decl->getTemplateParameters()) {
+ const auto *Param = dyn_cast<TemplateTypeParmDecl>(Parameter);
+ if (!Param) // some params are null
+ continue;
+ std::string Type;
+ if (Param->hasTypeConstraint())
+ Type = Param->getTypeConstraint()->getNamedConcept()->getName().str();
+ else if (Param->wasDeclaredWithTypename())
+ Type = "typename";
+ else
+ Type = "class";
+
+ addTemplateParameter(Type, Param->getName().str(), Param->getIndex(),
+ Param->getDepth(), Param->isParameterPack());
+ }
+ }
+
+ Template(const ClassTemplatePartialSpecializationDecl *Decl) {
+ for (auto *const Parameter : *Decl->getTemplateParameters()) {
+ const auto *Param = dyn_cast<TemplateTypeParmDecl>(Parameter);
+ if (!Param) // some params are null
+ continue;
+ std::string Type;
+ if (Param->hasTypeConstraint())
+ Type = Param->getTypeConstraint()->getNamedConcept()->getName().str();
+ else if (Param->wasDeclaredWithTypename())
+ Type = "typename";
+ else
+ Type = "class";
+
+ addTemplateParameter(Type, Param->getName().str(), Param->getIndex(),
+ Param->getDepth(), Param->isParameterPack());
+ }
+ }
+
+ const llvm::SmallVector<TemplateParameter> &getParameters() const {
+ return Parameters;
+ }
+
+ const llvm::SmallVector<TemplateConstraint> &getConstraints() const {
+ return Constraints;
+ }
+
+ void addTemplateParameter(std::string Type, std::string Name,
+ unsigned int Index, unsigned int Depth,
+ bool IsParameterPack) {
+ Parameters.emplace_back(Type, Name, Index, Depth, IsParameterPack);
+ }
+
+ bool empty() const { return Parameters.empty() && Constraints.empty(); }
+};
+
/// DocComment is a vector of RawComment::CommentLine.
///
/// Each line represents one line of striped documentation comment,
@@ -69,6 +149,10 @@ struct APIRecord {
RK_StaticField,
RK_CXXField,
RK_CXXClass,
+ RK_ClassTemplate,
+ RK_ClassTemplateSpecialization,
+ RK_ClassTemplatePartialSpecialization,
+ RK_Concept,
RK_CXXStaticMethod,
RK_CXXInstanceMethod,
RK_CXXConstructorMethod,
@@ -644,6 +728,75 @@ struct CXXClassRecord : APIRecord {
virtual void anchor();
};
+struct ClassTemplateRecord : CXXClassRecord {
+ Template Templ;
+
+ ClassTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+ AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading, Template Template,
+ bool IsFromSystemHeader)
+ : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment,
+ Declaration, SubHeading, RK_ClassTemplate,
+ IsFromSystemHeader),
+ Templ(Template) {}
+
+ static bool classof(const APIRecord *Record) {
+ return Record->getKind() == RK_ClassTemplate;
+ }
+};
+
+struct ClassTemplateSpecializationRecord : CXXClassRecord {
+ ClassTemplateSpecializationRecord(StringRef USR, StringRef Name,
+ PresumedLoc Loc,
+ AvailabilitySet Availabilities,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader)
+ : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment,
+ Declaration, SubHeading, RK_ClassTemplateSpecialization,
+ IsFromSystemHeader) {}
+
+ static bool classof(const APIRecord *Record) {
+ return Record->getKind() == RK_ClassTemplateSpecialization;
+ }
+};
+
+struct ClassTemplatePartialSpecializationRecord : CXXClassRecord {
+ Template Templ;
+ ClassTemplatePartialSpecializationRecord(
+ StringRef USR, StringRef Name, PresumedLoc Loc,
+ AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ Template Template, bool IsFromSystemHeader)
+ : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment,
+ Declaration, SubHeading, RK_ClassTemplateSpecialization,
+ IsFromSystemHeader),
+ Templ(Template) {}
+
+ static bool classof(const APIRecord *Record) {
+ return Record->getKind() == RK_ClassTemplatePartialSpecialization;
+ }
+};
+
+struct ConceptRecord : APIRecord {
+ Template Templ;
+ ConceptRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+ AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading, Template Template,
+ bool IsFromSystemHeader)
+ : APIRecord(RK_Concept, USR, Name, Loc, std::move(Availabilities),
+ LinkageInfo::none(), Comment, Declaration, SubHeading,
+ IsFromSystemHeader),
+ Templ(Template) {}
+
+ static bool classof(const APIRecord *Record) {
+ return Record->getKind() == RK_Concept;
+ }
+};
+
/// This holds information associated with Objective-C categories.
struct ObjCCategoryRecord : ObjCContainerRecord {
SymbolReference Interface;
@@ -779,6 +932,13 @@ 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 {};
+template <typename RecordTy> struct has_template : public std::false_type {};
+template <> struct has_template<ClassTemplateRecord> : public std::true_type {};
+template <>
+struct has_template<ClassTemplatePartialSpecializationRecord>
+ : public std::true_type {};
+template <> struct has_template<ConceptRecord> : public std::true_type {};
+
/// APISet holds the set of API records collected from given inputs.
class APISet {
public:
@@ -876,6 +1036,26 @@ class APISet {
DeclarationFragments Declaration, DeclarationFragments SubHeading,
APIRecord::RecordKind Kind, bool IsFromSystemHeader);
+ ClassTemplateRecord *
+ addClassTemplate(StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading, Template Template,
+ bool IsFromSystemHeader);
+
+ ClassTemplateSpecializationRecord *addClassTemplateSpecialization(
+ StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ bool IsFromSystemHeader);
+
+ ClassTemplatePartialSpecializationRecord *
+ addClassTemplatePartialSpecialization(
+ StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ Template Template, bool IsFromSystemHeader);
+
CXXMethodRecord *
addCXXMethod(CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
PresumedLoc Loc, AvailabilitySet Availability,
@@ -890,6 +1070,13 @@ class APISet {
FunctionSignature Signature, bool IsConstructor, AccessControl Access,
bool IsFromSystemHeader);
+ ConceptRecord *addConcept(StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading, Template Template,
+ 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
@@ -1018,6 +1205,19 @@ class APISet {
const RecordMap<EnumRecord> &getEnums() const { return Enums; }
const RecordMap<StructRecord> &getStructs() const { return Structs; }
const RecordMap<CXXClassRecord> &getCXXClasses() const { return CXXClasses; }
+ const RecordMap<ConceptRecord> &getConcepts() const { return Concepts; }
+ const RecordMap<ClassTemplateRecord> &getClassTemplates() const {
+ return ClassTemplates;
+ }
+ const RecordMap<ClassTemplateSpecializationRecord> &
+ getClassTemplateSpecializations() const {
+ return ClassTemplateSpecializations;
+ }
+ const RecordMap<ClassTemplatePartialSpecializationRecord> &
+ getClassTemplatePartialSpecializations() const {
+ return ClassTemplatePartialSpecializations;
+ }
+ const RecordMap<ConceptRecord> &getRecords() const { return Concepts; }
const RecordMap<ObjCCategoryRecord> &getObjCCategories() const {
return ObjCCategories;
}
@@ -1071,10 +1271,15 @@ class APISet {
llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
RecordMap<GlobalFunctionRecord> GlobalFunctions;
RecordMap<GlobalVariableRecord> GlobalVariables;
+ RecordMap<ConceptRecord> Concepts;
RecordMap<StaticFieldRecord> StaticFields;
RecordMap<EnumRecord> Enums;
RecordMap<StructRecord> Structs;
RecordMap<CXXClassRecord> CXXClasses;
+ RecordMap<ClassTemplateRecord> ClassTemplates;
+ RecordMap<ClassTemplateSpecializationRecord> ClassTemplateSpecializations;
+ RecordMap<ClassTemplatePartialSpecializationRecord>
+ ClassTemplatePartialSpecializations;
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 dc8a02e8811aed..c0271de91acfb2 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -22,6 +22,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/Lex/MacroInfo.h"
#include "llvm/ADT/StringRef.h"
#include <vector>
@@ -161,6 +162,11 @@ class DeclarationFragments {
return *this;
}
+ DeclarationFragments &pop_back() {
+ Fragments.pop_back();
+ return *this;
+ }
+
/// Append a text Fragment of a space character.
///
/// \returns a reference to the DeclarationFragments object itself after
@@ -292,6 +298,28 @@ class DeclarationFragmentsBuilder {
static DeclarationFragments
getFragmentsForOverloadedOperator(const CXXMethodDecl *);
+ static DeclarationFragments
+ getFragmentsForTemplateParameters(ArrayRef<NamedDecl *>);
+
+ static std::string getNameForTemplateArgument(const ArrayRef<NamedDecl *>,
+ std::string);
+
+ static DeclarationFragments
+ getFragmentsForTemplateArguments(const ArrayRef<TemplateArgument>,
+ ASTContext &,
+ const std::optional<ArrayRef<NamedDecl *>>);
+
+ static DeclarationFragments getFragmentsForConcept(const ConceptDecl *);
+
+ static DeclarationFragments
+ getFragmentsForRedeclarableTemplate(const RedeclarableTemplateDecl *);
+
+ static DeclarationFragments getFragmentsForClassTemplateSpecialization(
+ const ClassTemplateSpecializationDecl *);
+
+ static DeclarationFragments getFragmentsForClassTemplatePartialSpecialization(
+ const ClassTemplatePartialSpecializationDecl *);
+
/// Build DeclarationFragments for an Objective-C category declaration
/// ObjCCategoryDecl.
static DeclarationFragments
diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index 904942a69691a6..0392b6db2da0ad 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -15,6 +15,7 @@
#define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Specifiers.h"
#include "clang/ExtractAPI/DeclarationFragments.h"
@@ -52,10 +53,24 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
bool WalkUpFromCXXRecordDecl(const CXXRecordDecl *Decl);
+ bool WalkUpFromClassTemplateSpecializationDecl(
+ const ClassTemplateSpecializationDecl *Decl);
+
+ bool WalkUpFromClassTemplatePartialSpecializationDecl(
+ const ClassTemplatePartialSpecializationDecl *Decl);
+
bool VisitRecordDecl(const RecordDecl *Decl);
bool VisitCXXRecordDecl(const CXXRecordDecl *Decl);
+ bool VisitConceptDecl(const ConceptDecl *Decl);
+
+ bool VisitClassTemplateSpecializationDecl(
+ const ClassTemplateSpecializationDecl *Decl);
+
+ bool VisitClassTemplatePartialSpecializationDecl(
+ const ClassTemplatePartialSpecializationDecl *Decl);
+
bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl);
bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl);
@@ -128,6 +143,29 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
Derived &getDerivedExtractAPIVisitor() {
return *static_cast<Derived *>(this);
}
+
+ SmallVector<SymbolReference> getBases(const CXXRecordDecl *Decl) {
+ // FIXME: store AccessSpecifier given by inheritance
+ SmallVector<SymbolReference> Bases;
+ for (const auto BaseSpecifier : Decl->bases()) {
+ // skip classes not inherited as public
+ if (BaseSpecifier.getAccessSpecifier() != AccessSpecifier::AS_public)
+ continue;
+ SymbolReference BaseClass;
+ if (BaseSpecifier.getType().getTypePtr()->isTemplateTypeParmType()) {
+ BaseClass.Name = API.copyString(BaseSpecifier.getType().getAsString());
+ BaseClass.USR = API.recordUSR(
+ BaseSpecifier.getType()->getAs<TemplateTypeParmType>()->getDecl());
+ } else {
+ CXXRecordDecl *BaseClassDecl =
+ BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl();
+ BaseClass.Name = BaseClassDecl->getName();
+ BaseClass.USR = API.recordUSR(BaseClassDecl);
+ }
+ Bases.emplace_back(BaseClass);
+ }
+ return Bases;
+ }
};
template <typename T>
@@ -328,6 +366,22 @@ bool ExtractAPIVisitorBase<Derived>::WalkUpFromCXXRecordDecl(
return true;
}
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::WalkUpFromClassTemplateSpecializationDecl(
+ const ClassTemplateSpecializationDecl *Decl) {
+ getDerivedExtractAPIVisitor().VisitClassTemplateSpecializationDecl(Decl);
+ return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::
+ WalkUpFromClassTemplatePartialSpecializationDecl(
+ const ClassTemplatePartialSpecializationDecl *Decl) {
+ getDerivedExtractAPIVisitor().VisitClassTemplatePartialSpecializationDecl(
+ Decl);
+ return true;
+}
+
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
@@ -368,7 +422,8 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
const CXXRecordDecl *Decl) {
- if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
+ if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl) ||
+ Decl->isImplicit())
return true;
StringRef Name = Decl->getName();
@@ -393,22 +448,22 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
else
Kind = APIRecord::RecordKind::RK_CXXClass;
- CXXClassRecord *CXXClassRecord =
- API.addCXXClass(Name, USR, Loc, AvailabilitySet(Decl), Comment,
- Declaration, SubHeading, Kind, isInSystemHeader(Decl));
+ CXXClassRecord *CXXClassRecord;
+ if (Decl->getDescribedClassTemplate()) {
+ // Inject template fragments before class fragments.
+ Declaration.insert(
+ Declaration.begin(),
+ DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
+ Decl->getDescribedClassTemplate()));
+ CXXClassRecord = API.addClassTemplate(
+ Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
+ Template(Decl->getDescribedClassTemplate()), isInSystemHeader(Decl));
+ } else
+ 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);
- }
+ CXXClassRecord->Bases = getBases(Decl);
getDerivedExtractAPIVisitor().recordCXXFields(CXXClassRecord, Decl->fields());
getDerivedExtractAPIVisitor().recordCXXMethods(CXXClassRecord,
@@ -416,6 +471,97 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
return true;
}
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitConceptDecl(const ConceptDecl *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::getFragmentsForConcept(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+ API.addConcept(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
+ SubHeading, Template(Decl), isInSystemHeader(Decl));
+ return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitClassTemplateSpecializationDecl(
+ const ClassTemplateSpecializationDecl *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::getFragmentsForClassTemplateSpecialization(
+ Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ auto *ClassTemplateSpecializationRecord = API.addClassTemplateSpecialization(
+ Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading,
+ isInSystemHeader(Decl));
+
+ ClassTemplateSpecializationRecord->Bases = getBases(Decl);
+ getDerivedExtractAPIVisitor().recordCXXFields(
+ ClassTemplateSpecializationRecord, Decl->fields());
+ getDerivedExtractAPIVisitor().recordCXXMethods(
+ ClassTemplateSpecializationRecord, Decl->methods());
+ return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::
+ VisitClassTemplatePartialSpecializationDecl(
+ const ClassTemplatePartialSpecializationDecl *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::
+ getFragmentsForClassTemplatePartialSpecialization(Decl);
+ DeclarationFragments SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ auto *ClassTemplatePartialSpecRecord =
+ API.addClassTemplatePartialSpecialization(
+ Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration,
+ SubHeading, Template(Decl), isInSystemHeader(Decl));
+
+ ClassTemplatePartialSpecRecord->Bases = getBases(Decl);
+
+ getDerivedExtractAPIVisitor().recordCXXFields(ClassTemplatePartialSpecRecord,
+ Decl->fields());
+ getDerivedExtractAPIVisitor().recordCXXMethods(ClassTemplatePartialSpecRecord,
+ Decl->methods());
+ return true;
+}
+
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
const ObjCInterfaceDecl *Decl) {
diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
index 68c20b3f6c6f9d..2bcd480b60e1c9 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -33,6 +33,14 @@ template <typename Derived> class APISetVisitor {
getDerived()->traverseCXXClassRecords();
+ getDerived()->traverseClassTemplateRecords();
+
+ getDerived()->traverseClassTemplateSpecializationRecords();
+
+ getDerived()->traverseClassTemplatePartialSpecializationRecords();
+
+ getDerived()->traverseConcepts();
+
getDerived()->traverseStructRecords();
getDerived()->traverseObjCInterfaces();
@@ -76,6 +84,30 @@ template <typename Derived> class APISetVisitor {
getDerived()->visitCXXClassRecord(*Class.second);
}
+ void traverseClassTemplateRecords() {
+ for (const auto &ClassTemplate : API.getClassTemplates())
+ getDerived()->visitClassTemplateRecord(*ClassTemplate.second);
+ }
+
+ void traverseClassTemplateSpecializationRecords() {
+ for (const auto &ClassTemplateSpecialization :
+ API.getClassTemplateSpecializations())
+ getDerived()->visitClassTemplateSpecializationRecord(
+ *ClassTemplateSpecialization.second);
+ }
+
+ void traverseClassTemplatePartialSpecializationRecords() {
+ for (const auto &ClassTemplatePartialSpecialization :
+ API.getClassTemplatePartialSpecializations())
+ getDerived()->visitClassTemplatePartialSpecializationRecord(
+ *ClassTemplatePartialSpecialization.second);
+ }
+
+ void traverseConcepts() {
+ for (const auto &Concept : API.getConcepts())
+ getDerived()->visitConceptRecord(*Concept.second);
+ }
+
void traverseObjCInterfaces() {
for (const auto &Interface : API.getObjCInterfaces())
getDerived()->visitObjCContainerRecord(*Interface.second);
@@ -117,6 +149,14 @@ template <typename Derived> class APISetVisitor {
void visitCXXClassRecord(const CXXClassRecord &Record){};
+ void visitClassTemplateRecord(const ClassTemplateRecord &Record){};
+
+ void visitClassTemplateSpecializationRecord(
+ const ClassTemplateSpecializationRecord &Record){};
+
+ void visitClassTemplatePartialSpecializationRecord(
+ const ClassTemplatePartialSpecializationRecord &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 ab60b5c71f2289..7b315c9f6d410e 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -97,6 +97,10 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
/// Get the string representation of the relationship kind.
static StringRef getRelationshipString(RelationshipKind Kind);
+ enum ConstraintKind { Conformance, ConditionalConformance };
+
+ static StringRef getConstraintString(ConstraintKind Kind);
+
private:
/// Just serialize the currently recorded objects in Symbol Graph format.
Object serializeCurrentGraph();
@@ -171,6 +175,16 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
void visitCXXClassRecord(const CXXClassRecord &Record);
+ void visitClassTemplateRecord(const ClassTemplateRecord &Record);
+
+ void visitClassTemplateSpecializationRecord(
+ const ClassTemplateSpecializationRecord &Record);
+
+ void visitClassTemplatePartialSpecializationRecord(
+ const ClassTemplatePartialSpecializationRecord &Record);
+
+ void visitConceptRecord(const ConceptRecord &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 d9baa188f1be5b..c1c4bc7a0b4cf7 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -159,6 +159,50 @@ APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc,
SubHeading, Kind, IsFromSystemHeader);
}
+ClassTemplateRecord *APISet::addClassTemplate(
+ StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ Template Template, bool IsFromSystemHeader) {
+
+ return addTopLevelRecord(USRBasedLookupTable, ClassTemplates, USR, Name, Loc,
+ std::move(Availability), Comment, Declaration,
+ SubHeading, Template, IsFromSystemHeader);
+}
+
+ClassTemplateSpecializationRecord *APISet::addClassTemplateSpecialization(
+ StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, ClassTemplateSpecializations,
+ USR, Name, Loc, std::move(Availability), Comment,
+ Declaration, SubHeading, IsFromSystemHeader);
+}
+
+ClassTemplatePartialSpecializationRecord *
+APISet::addClassTemplatePartialSpecialization(
+ StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ Template Template, bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable,
+ ClassTemplatePartialSpecializations, USR, Name, Loc,
+ std::move(Availability), Comment, Declaration,
+ SubHeading, Template, IsFromSystemHeader);
+}
+
+ConceptRecord *APISet::addConcept(StringRef Name, StringRef USR,
+ PresumedLoc Loc, AvailabilitySet Availability,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading,
+ Template Template, bool IsFromSystemHeader) {
+ return addTopLevelRecord(USRBasedLookupTable, Concepts, USR, Name, Loc,
+ std::move(Availability), Comment, Declaration,
+ SubHeading, Template, IsFromSystemHeader);
+}
+
CXXMethodRecord *APISet::addCXXMethod(
CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR,
PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment,
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 703142a910764c..ec8ae56f71e161 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -735,6 +735,174 @@ DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator(
return Fragments.append(";", DeclarationFragments::FragmentKind::Text);
}
+// Get fragments for template parameters, e.g. T in tempalte<typename T> ...
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForTemplateParameters(
+ ArrayRef<NamedDecl *> ParameterArray) {
+ DeclarationFragments Fragments;
+ for (unsigned i = 0, end = ParameterArray.size(); i != end; ++i) {
+ if (i)
+ Fragments.append(",", DeclarationFragments::FragmentKind::Text)
+ .appendSpace();
+
+ const auto *TemplateParam =
+ dyn_cast<TemplateTypeParmDecl>(ParameterArray[i]);
+ if (!TemplateParam)
+ continue;
+ if (TemplateParam->hasTypeConstraint())
+ Fragments.append(TemplateParam->getTypeConstraint()
+ ->getNamedConcept()
+ ->getName()
+ .str(),
+ DeclarationFragments::FragmentKind::TypeIdentifier);
+ else if (TemplateParam->wasDeclaredWithTypename())
+ Fragments.append("typename", DeclarationFragments::FragmentKind::Keyword);
+ else
+ Fragments.append("class", DeclarationFragments::FragmentKind::Keyword);
+
+ if (TemplateParam->isParameterPack())
+ Fragments.append("...", DeclarationFragments::FragmentKind::Text);
+
+ Fragments.appendSpace().append(
+ TemplateParam->getName(),
+ DeclarationFragments::FragmentKind::GenericParameter);
+ }
+ return Fragments;
+}
+
+// Find the name of a template argument from the template's parameters.
+std::string DeclarationFragmentsBuilder::getNameForTemplateArgument(
+ const ArrayRef<NamedDecl *> TemplateParameters, std::string TypeParameter) {
+ // The arg is a generic parameter from a partial spec, e.g.
+ // T in template<typename T> Foo<T, int>.
+ //
+ // Those names appear as "type-parameter-<index>-<depth>", so we must find its
+ // name from the template's parameter list.
+ for (unsigned i = 0; i < TemplateParameters.size(); ++i) {
+ const auto *Parameter =
+ dyn_cast<TemplateTypeParmDecl>(TemplateParameters[i]);
+ if (TypeParameter.compare("type-parameter-" +
+ std::to_string(Parameter->getDepth()) + "-" +
+ std::to_string(Parameter->getIndex())) == 0)
+ return std::string(TemplateParameters[i]->getName());
+ }
+ llvm_unreachable("Could not find the name of a template argument.");
+}
+
+// Get fragments for template arguments, e.g. int in template<typename T>
+// Foo<int>;
+//
+// Note: TemplateParameters is only necessary if the Decl is a
+// PartialSpecialization, where we need the parameters to deduce the name of the
+// generic arguments.
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForTemplateArguments(
+ const ArrayRef<TemplateArgument> TemplateArguments, ASTContext &Context,
+ const std::optional<ArrayRef<NamedDecl *>> TemplateParameters) {
+ DeclarationFragments Fragments;
+ for (unsigned i = 0, end = TemplateArguments.size(); i != end; ++i) {
+ if (i)
+ Fragments.append(",", DeclarationFragments::FragmentKind::Text)
+ .appendSpace();
+
+ std::string Type = TemplateArguments[i].getAsType().getAsString();
+ DeclarationFragments After;
+ DeclarationFragments ArgumentFragment =
+ getFragmentsForType(TemplateArguments[i].getAsType(), Context, After);
+
+ if (ArgumentFragment.begin()->Spelling.substr(0, 14).compare(
+ "type-parameter") == 0) {
+ std::string ProperArgName = getNameForTemplateArgument(
+ TemplateParameters.value(), ArgumentFragment.begin()->Spelling);
+ ArgumentFragment.begin()->Spelling.swap(ProperArgName);
+ }
+ Fragments.append(std::move(ArgumentFragment));
+
+ if (TemplateArguments[i].isPackExpansion())
+ Fragments.append("...", DeclarationFragments::FragmentKind::Text);
+ }
+ return Fragments;
+}
+
+DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForConcept(
+ const ConceptDecl *Concept) {
+ DeclarationFragments Fragments;
+ return Fragments
+ .append("template", DeclarationFragments::FragmentKind::Keyword)
+ .append("<", DeclarationFragments::FragmentKind::Text)
+ .append(getFragmentsForTemplateParameters(
+ Concept->getTemplateParameters()->asArray()))
+ .append("> ", DeclarationFragments::FragmentKind::Text)
+ .append("concept", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace()
+ .append(Concept->getName().str(),
+ DeclarationFragments::FragmentKind::Identifier)
+ .append(";", DeclarationFragments::FragmentKind::Text);
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
+ const RedeclarableTemplateDecl *RedeclarableTemplate) {
+ DeclarationFragments Fragments;
+ Fragments.append("template", DeclarationFragments::FragmentKind::Keyword)
+ .append("<", DeclarationFragments::FragmentKind::Text)
+ .append(getFragmentsForTemplateParameters(
+ RedeclarableTemplate->getTemplateParameters()->asArray()))
+ .append(">", DeclarationFragments::FragmentKind::Text)
+ .appendSpace();
+
+ if (isa<TypeAliasTemplateDecl>(RedeclarableTemplate))
+ Fragments.appendSpace()
+ .append("using", DeclarationFragments::FragmentKind::Keyword)
+ .appendSpace()
+ .append(RedeclarableTemplate->getName(),
+ DeclarationFragments::FragmentKind::Identifier);
+ // the templated records will be resposbible for injecting their templates
+ return Fragments.appendSpace();
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization(
+ const ClassTemplateSpecializationDecl *Decl) {
+ DeclarationFragments Fragments;
+ return Fragments
+ .append("template", DeclarationFragments::FragmentKind::Keyword)
+ .append("<", DeclarationFragments::FragmentKind::Text)
+ .append(">", DeclarationFragments::FragmentKind::Text)
+ .appendSpace()
+ .append(DeclarationFragmentsBuilder::getFragmentsForCXXClass(
+ cast<CXXRecordDecl>(Decl)))
+ .pop_back() // there is an extra semicolon now
+ .append("<", DeclarationFragments::FragmentKind::Text)
+ .append(
+ getFragmentsForTemplateArguments(Decl->getTemplateArgs().asArray(),
+ Decl->getASTContext(), std::nullopt))
+ .append(">", DeclarationFragments::FragmentKind::Text)
+ .append(";", DeclarationFragments::FragmentKind::Text);
+}
+
+DeclarationFragments
+DeclarationFragmentsBuilder::getFragmentsForClassTemplatePartialSpecialization(
+ const ClassTemplatePartialSpecializationDecl *Decl) {
+ DeclarationFragments Fragments;
+ return Fragments
+ .append("template", DeclarationFragments::FragmentKind::Keyword)
+ .append("<", DeclarationFragments::FragmentKind::Text)
+ .append(getFragmentsForTemplateParameters(
+ Decl->getTemplateParameters()->asArray()))
+ .append(">", DeclarationFragments::FragmentKind::Text)
+ .appendSpace()
+ .append(DeclarationFragmentsBuilder::getFragmentsForCXXClass(
+ cast<CXXRecordDecl>(Decl)))
+ .pop_back() // there is an extra semicolon now
+ .append("<", DeclarationFragments::FragmentKind::Text)
+ .append(getFragmentsForTemplateArguments(
+ Decl->getTemplateArgs().asArray(), Decl->getASTContext(),
+ Decl->getTemplateParameters()->asArray()))
+ .append(">", DeclarationFragments::FragmentKind::Text)
+ .append(";", DeclarationFragments::FragmentKind::Text);
+}
+
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
const MacroDirective *MD) {
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index af52abe9f2e144..6c7a037f76e348 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -393,10 +393,17 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
Kind["identifier"] = AddLangPrefix("type.property");
Kind["displayName"] = "Type Property";
break;
+ case APIRecord::RK_ClassTemplate:
+ case APIRecord::RK_ClassTemplateSpecialization:
+ case APIRecord::RK_ClassTemplatePartialSpecialization:
case APIRecord::RK_CXXClass:
Kind["identifier"] = AddLangPrefix("class");
Kind["displayName"] = "Class";
break;
+ case APIRecord::RK_Concept:
+ Kind["identifier"] = AddLangPrefix("concept");
+ Kind["displayName"] = "Concept";
+ break;
case APIRecord::RK_CXXStaticMethod:
Kind["identifier"] = AddLangPrefix("type.method");
Kind["displayName"] = "Static Method";
@@ -545,6 +552,52 @@ void serializeAccessMixin(Object &Paren, const RecordTy &Record) {
serializeString(Paren, "accessLevel", accessLevel);
}
+template <typename RecordTy>
+std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record,
+ std::true_type) {
+ const auto &Template = Record.Templ;
+ if (Template.empty())
+ return std::nullopt;
+
+ Object Generics;
+ Array GenericParameters;
+ for (const auto Param : Template.getParameters()) {
+ Object Parameter;
+ Parameter["name"] = Param.Name;
+ Parameter["index"] = Param.Index;
+ Parameter["depth"] = Param.Depth;
+ GenericParameters.emplace_back(std::move(Parameter));
+ }
+ if (!GenericParameters.empty())
+ Generics["parameters"] = std::move(GenericParameters);
+
+ Array GenericConstraints;
+ for (const auto Constr : Template.getConstraints()) {
+ Object Constraint;
+ Constraint["kind"] = Constr.Kind;
+ Constraint["lhs"] = Constr.LHS;
+ Constraint["rhs"] = Constr.RHS;
+ GenericConstraints.emplace_back(std::move(Constraint));
+ }
+
+ if (!GenericConstraints.empty())
+ Generics["constraints"] = std::move(GenericConstraints);
+
+ return Generics;
+}
+
+template <typename RecordTy>
+std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record,
+ std::false_type) {
+ return std::nullopt;
+}
+
+template <typename RecordTy>
+void serializeTemplateMixin(Object &Paren, const RecordTy &Record) {
+ serializeObject(Paren, "swiftGenerics",
+ serializeTemplateMixinImpl(Record, has_template<RecordTy>()));
+}
+
struct PathComponent {
StringRef USR;
StringRef Name;
@@ -691,6 +744,7 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const {
serializeFunctionSignatureMixin(Obj, Record);
serializeAccessMixin(Obj, Record);
+ serializeTemplateMixin(Obj, Record);
return Obj;
}
@@ -726,6 +780,16 @@ StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) {
llvm_unreachable("Unhandled relationship kind");
}
+StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) {
+ switch (Kind) {
+ case ConstraintKind::Conformance:
+ return "conformance";
+ case ConstraintKind::ConditionalConformance:
+ return "conditionalConformance";
+ }
+ llvm_unreachable("Unhandled constraint kind");
+}
+
void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
SymbolReference Source,
SymbolReference Target) {
@@ -796,6 +860,56 @@ void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) {
serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
}
+void SymbolGraphSerializer::visitClassTemplateRecord(
+ const ClassTemplateRecord &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::visitClassTemplateSpecializationRecord(
+ const ClassTemplateSpecializationRecord &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::visitClassTemplatePartialSpecializationRecord(
+ const ClassTemplatePartialSpecializationRecord &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::visitConceptRecord(const ConceptRecord &Record) {
+ auto Concept = serializeAPIRecord(Record);
+ if (!Concept)
+ return;
+
+ Symbols.emplace_back(std::move(*Concept));
+}
+
void SymbolGraphSerializer::visitObjCContainerRecord(
const ObjCContainerRecord &Record) {
auto ObjCContainer = serializeAPIRecord(Record);
diff --git a/clang/test/ExtractAPI/class_template.cpp b/clang/test/ExtractAPI/class_template.cpp
new file mode 100644
index 00000000000000..25895a6fefe06d
--- /dev/null
+++ b/clang/test/ExtractAPI/class_template.cpp
@@ -0,0 +1,133 @@
+// 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
+template<typename T> class 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": [],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": "> "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@ST>1#T at Foo"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 28,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "swiftGenerics": {
+ "parameters": [
+ {
+ "depth": 0,
+ "index": 0,
+ "name": "T"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/class_template_param_inheritance.cpp b/clang/test/ExtractAPI/class_template_param_inheritance.cpp
new file mode 100644
index 00000000000000..0e50e6081c914d
--- /dev/null
+++ b/clang/test/ExtractAPI/class_template_param_inheritance.cpp
@@ -0,0 +1,140 @@
+// 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
+template<typename T> class Foo : public T {};
+
+/// 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:@ST>1#T at Foo",
+ "target": "",
+ "targetFallback": "T"
+ }
+ ],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": "> "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@ST>1#T at Foo"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 28,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "swiftGenerics": {
+ "parameters": [
+ {
+ "depth": 0,
+ "index": 0,
+ "name": "T"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/class_template_partial_spec.cpp b/clang/test/ExtractAPI/class_template_partial_spec.cpp
new file mode 100644
index 00000000000000..294bbb726bcf08
--- /dev/null
+++ b/clang/test/ExtractAPI/class_template_partial_spec.cpp
@@ -0,0 +1,261 @@
+// 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
+template<typename X, typename Y> class Foo {};
+
+template<typename Z> class Foo<Z, int> {};
+
+/// 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": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "X"
+ },
+ {
+ "kind": "text",
+ "spelling": ", "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "Y"
+ },
+ {
+ "kind": "text",
+ "spelling": "> "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@ST>2#T#T at Foo"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 40,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "swiftGenerics": {
+ "parameters": [
+ {
+ "depth": 0,
+ "index": 0,
+ "name": "X"
+ },
+ {
+ "depth": 0,
+ "index": 1,
+ "name": "Y"
+ }
+ ]
+ }
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "Z"
+ },
+ {
+ "kind": "text",
+ "spelling": "> "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:t0.0",
+ "spelling": "Z"
+ },
+ {
+ "kind": "text",
+ "spelling": ", "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": ">;"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@SP>1#T at Foo>#t0.0#I"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 28,
+ "line": 3
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "swiftGenerics": {
+ "parameters": [
+ {
+ "depth": 0,
+ "index": 0,
+ "name": "Z"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/class_template_spec.cpp b/clang/test/ExtractAPI/class_template_spec.cpp
new file mode 100644
index 00000000000000..166b03911db7dc
--- /dev/null
+++ b/clang/test/ExtractAPI/class_template_spec.cpp
@@ -0,0 +1,206 @@
+// 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
+template<typename T> class Foo {};
+
+template<> class Foo<int> {};
+
+/// 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": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": "> "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@ST>1#T at Foo"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 28,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "swiftGenerics": {
+ "parameters": [
+ {
+ "depth": 0,
+ "index": 0,
+ "name": "T"
+ }
+ ]
+ }
+ },
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<> "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": ">;"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Foo>#I"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 18,
+ "line": 3
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ]
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/concept.cpp b/clang/test/ExtractAPI/concept.cpp
new file mode 100644
index 00000000000000..08c3c832ce3a06
--- /dev/null
+++ b/clang/test/ExtractAPI/concept.cpp
@@ -0,0 +1,133 @@
+// 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 -std=c++20 -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
+template<typename T> concept Foo = true;
+
+/// 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": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": "> "
+ },
+ {
+ "kind": "keyword",
+ "spelling": "concept"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@CT at Foo"
+ },
+ "kind": {
+ "displayName": "Concept",
+ "identifier": "c++.concept"
+ },
+ "location": {
+ "position": {
+ "character": 30,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ],
+ "swiftGenerics": {
+ "parameters": [
+ {
+ "depth": 0,
+ "index": 0,
+ "name": "T"
+ }
+ ]
+ }
+ }
+ ]
+}
More information about the cfe-commits
mailing list