[clang][ExtractAPI] Add support for C++ member templates

Visit and serialize C++ fields by checking if a var template's context is a CXXRecordDecl in VisitVarTemplateDecl.

Depends on D158027

Reviewed By: dang

Differential Revision: https://reviews.llvm.org/D158029
This commit is contained in:
Erick Velez 2023-08-21 09:06:53 -07:00
parent 0303137bfc
commit 634b2fd2ca
8 changed files with 308 additions and 11 deletions

View File

@ -171,6 +171,7 @@ struct APIRecord {
RK_Union,
RK_StaticField,
RK_CXXField,
RK_CXXFieldTemplate,
RK_CXXClass,
RK_ClassTemplate,
RK_ClassTemplateSpecialization,
@ -530,6 +531,25 @@ private:
virtual void anchor();
};
struct CXXFieldTemplateRecord : CXXFieldRecord {
Template Templ;
CXXFieldTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading, AccessControl Access,
Template Template, bool IsFromSystemHeader)
: CXXFieldRecord(RK_CXXFieldTemplate, USR, Name, Loc,
std::move(Availabilities), Comment, Declaration,
SubHeading, Access, IsFromSystemHeader),
Templ(Template) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_CXXFieldTemplate;
}
};
struct CXXMethodRecord : APIRecord {
FunctionSignature Signature;
AccessControl Access;
@ -1113,6 +1133,8 @@ struct has_access<CXXMethodTemplateRecord> : public std::true_type {};
template <>
struct has_access<CXXMethodTemplateSpecializationRecord>
: public std::true_type {};
template <>
struct has_access<CXXFieldTemplateRecord> : public std::true_type {};
template <typename RecordTy> struct has_template : public std::false_type {};
template <> struct has_template<ClassTemplateRecord> : public std::true_type {};
@ -1127,6 +1149,8 @@ struct has_template<GlobalVariableTemplatePartialSpecializationRecord>
: public std::true_type {};
template <>
struct has_template<CXXMethodTemplateRecord> : public std::true_type {};
template <>
struct has_template<CXXFieldTemplateRecord> : public std::true_type {};
template <>
struct has_template<GlobalFunctionTemplateRecord> : public std::true_type {};
@ -1251,6 +1275,12 @@ public:
DeclarationFragments SubHeading,
AccessControl Access, bool IsFromSystemHeader);
CXXFieldTemplateRecord *addCXXFieldTemplate(
APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading,
AccessControl Access, Template Template, bool IsFromSystemHeader);
CXXClassRecord *
addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, const DocComment &Comment,
@ -1482,6 +1512,9 @@ public:
getCXXMethodTemplateSpecializations() const {
return CXXMethodTemplateSpecializations;
}
const RecordMap<CXXFieldTemplateRecord> &getCXXFieldTemplates() const {
return CXXFieldTemplates;
}
const RecordMap<ConceptRecord> &getConcepts() const { return Concepts; }
const RecordMap<ClassTemplateRecord> &getClassTemplates() const {
return ClassTemplates;
@ -1564,6 +1597,7 @@ private:
RecordMap<CXXMethodTemplateRecord> CXXMethodTemplates;
RecordMap<CXXMethodTemplateSpecializationRecord>
CXXMethodTemplateSpecializations;
RecordMap<CXXFieldTemplateRecord> CXXFieldTemplates;
RecordMap<ClassTemplateRecord> ClassTemplates;
RecordMap<ClassTemplateSpecializationRecord> ClassTemplateSpecializations;
RecordMap<ClassTemplatePartialSpecializationRecord>

View File

@ -697,20 +697,29 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplateDecl(
Context.getDiagnostics());
// Build declaration fragments and sub-heading for the variable.
DeclarationFragments Declaration =
DeclarationFragmentsBuilder::getFragmentsForVarTemplate(
Decl->getTemplatedDecl());
DeclarationFragments Declaration;
Declaration
.append(DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
Decl))
.append(DeclarationFragmentsBuilder::getFragmentsForVarTemplate(
Decl->getTemplatedDecl()));
// Inject template fragments before var fragments.
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);
// Inject template fragments before var fragments.
Declaration.insert(
Declaration.begin(),
DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(Decl));
API.addGlobalVariableTemplate(Name, USR, Loc, AvailabilitySet(Decl), Linkage,
Comment, Declaration, SubHeading,
Template(Decl), isInSystemHeader(Decl));
SmallString<128> ParentUSR;
index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
ParentUSR);
if (Decl->getDeclContext()->getDeclKind() == Decl::CXXRecord)
API.addCXXFieldTemplate(API.findRecordForUSR(ParentUSR), Name, USR, Loc,
AvailabilitySet(Decl), Comment, Declaration,
SubHeading,
DeclarationFragmentsBuilder::getAccessControl(Decl),
Template(Decl), isInSystemHeader(Decl));
else
API.addGlobalVariableTemplate(Name, USR, Loc, AvailabilitySet(Decl),
Linkage, Comment, Declaration, SubHeading,
Template(Decl), isInSystemHeader(Decl));
return true;
}

View File

@ -43,6 +43,8 @@ public:
getDerived()->traverseCXXMethodTemplateSpecializations();
getDerived()->traverseCXXFieldTemplates();
getDerived()->traverseConcepts();
getDerived()->traverseGlobalVariableTemplateRecords();
@ -129,6 +131,11 @@ public:
*ClassTemplatePartialSpecialization.second);
}
void traverseCXXFieldTemplates() {
for (const auto &CXXFieldTemplate : API.getCXXFieldTemplates())
getDerived()->visitCXXFieldTemplateRecord(*CXXFieldTemplate.second);
}
void traverseGlobalVariableTemplateRecords() {
for (const auto &GlobalVariableTemplate : API.getGlobalVariableTemplates())
getDerived()->visitGlobalVariableTemplateRecord(
@ -221,6 +228,8 @@ public:
void visitMethodTemplateSpecializationRecord(
const CXXMethodTemplateSpecializationRecord &Record){};
void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record){};
void visitGlobalVariableTemplateRecord(
const GlobalVariableTemplateRecord &Record) {}

View File

@ -188,6 +188,8 @@ public:
void visitMethodTemplateSpecializationRecord(
const CXXMethodTemplateSpecializationRecord &Record);
void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record);
void visitConceptRecord(const ConceptRecord &Record);
void

View File

@ -185,6 +185,21 @@ APISet::addCXXField(CXXClassRecord *CXXClass, StringRef Name, StringRef USR,
return CXXClass->Fields.emplace_back(std::move(Record)).get();
}
CXXFieldTemplateRecord *APISet::addCXXFieldTemplate(
APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, const DocComment &Comment,
DeclarationFragments Declaration, DeclarationFragments SubHeading,
AccessControl Access, Template Template, bool IsFromSystemHeader) {
auto *Record =
addTopLevelRecord(USRBasedLookupTable, CXXFieldTemplates, USR, Name, Loc,
std::move(Availability), Comment, Declaration,
SubHeading, Access, Template, IsFromSystemHeader);
Record->ParentInformation = APIRecord::HierarchyInformation(
Parent->USR, Parent->Name, Parent->getKind(), Parent);
return Record;
}
CXXClassRecord *
APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities, const DocComment &Comment,

View File

@ -465,6 +465,11 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplate(const VarDecl *Var) {
? Var->getTypeSourceInfo()->getType()
: Var->getASTContext().getUnqualifiedObjCPointerType(Var->getType());
// Might be a member, so might be static.
if (Var->isStaticDataMember())
Fragments.append("static", DeclarationFragments::FragmentKind::Keyword)
.appendSpace();
DeclarationFragments After;
DeclarationFragments ArgumentFragment =
getFragmentsForType(T, Var->getASTContext(), After);

View File

@ -428,6 +428,10 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
Kind["identifier"] = AddLangPrefix("method");
Kind["displayName"] = "Method Template Specialization";
break;
case APIRecord::RK_CXXFieldTemplate:
Kind["identifier"] = AddLangPrefix("property");
Kind["displayName"] = "Template Property";
break;
case APIRecord::RK_Concept:
Kind["identifier"] = AddLangPrefix("concept");
Kind["displayName"] = "Concept";
@ -956,6 +960,19 @@ void SymbolGraphSerializer::visitMethodTemplateSpecializationRecord(
Record.ParentInformation.ParentRecord);
}
void SymbolGraphSerializer::visitCXXFieldTemplateRecord(
const CXXFieldTemplateRecord &Record) {
if (!ShouldRecurse)
// Ignore child symbols
return;
auto CXXFieldTemplate = serializeAPIRecord(Record);
if (!CXXFieldTemplate)
return;
Symbols.emplace_back(std::move(*CXXFieldTemplate));
serializeRelationship(RelationshipKind::MemberOf, Record,
Record.ParentInformation.ParentRecord);
}
void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) {
auto Concept = serializeAPIRecord(Record);
if (!Concept)

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
class Foo {
template<typename T> static T Bar;
};
/// expected-no-diagnostics
//--- reference.output.json.in
{
"metadata": {
"formatVersion": {
"major": 0,
"minor": 5,
"patch": 3
},
"generator": "?"
},
"module": {
"name": "",
"platform": {
"architecture": "arm64",
"operatingSystem": {
"minimumVersion": {
"major": 11,
"minor": 0,
"patch": 0
},
"name": "macosx"
},
"vendor": "apple"
}
},
"relationships": [
{
"kind": "memberOf",
"source": "c:@S@Foo@Bar",
"target": "c:@S@Foo",
"targetFallback": "Foo"
}
],
"symbols": [
{
"accessLevel": "public",
"declarationFragments": [
{
"kind": "keyword",
"spelling": "class"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo"
},
"kind": {
"displayName": "Class",
"identifier": "c++.class"
},
"location": {
"position": {
"character": 7,
"line": 1
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
},
{
"accessLevel": "private",
"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": "static"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:t0.0",
"spelling": "T"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Bar"
},
{
"kind": "text",
"spelling": ";"
}
],
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@S@Foo@Bar"
},
"kind": {
"displayName": "Template Property",
"identifier": "c++.property"
},
"location": {
"position": {
"character": 33,
"line": 2
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Bar"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Bar"
}
],
"title": "Bar"
},
"pathComponents": [
"Foo",
"Bar"
],
"swiftGenerics": {
"parameters": [
{
"depth": 0,
"index": 0,
"name": "T"
}
]
}
}
]
}