[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
This commit is contained in:
Erick Velez 2023-08-18 12:03:44 -07:00
parent d8900f661a
commit 7ba37f4e46
13 changed files with 1648 additions and 16 deletions

View File

@ -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 @@ private:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ private:
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;

View File

@ -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 @@ public:
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 @@ public:
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

View File

@ -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 @@ public:
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 @@ private:
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) {

View File

@ -33,6 +33,14 @@ public:
getDerived()->traverseCXXClassRecords();
getDerived()->traverseClassTemplateRecords();
getDerived()->traverseClassTemplateSpecializationRecords();
getDerived()->traverseClassTemplatePartialSpecializationRecords();
getDerived()->traverseConcepts();
getDerived()->traverseStructRecords();
getDerived()->traverseObjCInterfaces();
@ -76,6 +84,30 @@ public:
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 @@ public:
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){};

View File

@ -97,6 +97,10 @@ public:
/// 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 @@ public:
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);

View File

@ -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,

View File

@ -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) {

View File

@ -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);

View File

@ -0,0 +1,133 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang_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@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"
}
]
}
}
]
}

View File

@ -0,0 +1,140 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang_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@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@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"
}
]
}
}
]
}

View File

@ -0,0 +1,261 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang_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@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@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"
}
]
}
}
]
}

View File

@ -0,0 +1,206 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang_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@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@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"
]
}
]
}

View File

@ -0,0 +1,133 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \
// RUN: %t/reference.output.json.in >> %t/reference.output.json
// RUN: %clang_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@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"
}
]
}
}
]
}