[clang] 634b2fd - [clang][ExtractAPI] Add support for C++ member templates
Erick Velez via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 21 10:18:37 PDT 2023
Author: Erick Velez
Date: 2023-08-21T10:17:58-07:00
New Revision: 634b2fd2cac251e2d645dadc6b00a2b2b80a08d0
URL: https://github.com/llvm/llvm-project/commit/634b2fd2cac251e2d645dadc6b00a2b2b80a08d0
DIFF: https://github.com/llvm/llvm-project/commit/634b2fd2cac251e2d645dadc6b00a2b2b80a08d0.diff
LOG: [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
Added:
clang/test/ExtractAPI/field_template.cpp
Modified:
clang/include/clang/ExtractAPI/API.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 cd20de6674f708..b855f460b9c7c1 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -171,6 +171,7 @@ struct APIRecord {
RK_Union,
RK_StaticField,
RK_CXXField,
+ RK_CXXFieldTemplate,
RK_CXXClass,
RK_ClassTemplate,
RK_ClassTemplateSpecialization,
@@ -530,6 +531,25 @@ struct CXXFieldRecord : APIRecord {
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 @@ class APISet {
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 @@ class APISet {
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 @@ class APISet {
RecordMap<CXXMethodTemplateRecord> CXXMethodTemplates;
RecordMap<CXXMethodTemplateSpecializationRecord>
CXXMethodTemplateSpecializations;
+ RecordMap<CXXFieldTemplateRecord> CXXFieldTemplates;
RecordMap<ClassTemplateRecord> ClassTemplates;
RecordMap<ClassTemplateSpecializationRecord> ClassTemplateSpecializations;
RecordMap<ClassTemplatePartialSpecializationRecord>
diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index 5dfffb3e8fd60a..dbe7f78cd33430 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -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;
}
diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
index dcf395f68d951a..34bb83a1382339 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -43,6 +43,8 @@ template <typename Derived> class APISetVisitor {
getDerived()->traverseCXXMethodTemplateSpecializations();
+ getDerived()->traverseCXXFieldTemplates();
+
getDerived()->traverseConcepts();
getDerived()->traverseGlobalVariableTemplateRecords();
@@ -129,6 +131,11 @@ template <typename Derived> class APISetVisitor {
*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 @@ template <typename Derived> class APISetVisitor {
void visitMethodTemplateSpecializationRecord(
const CXXMethodTemplateSpecializationRecord &Record){};
+ void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record){};
+
void visitGlobalVariableTemplateRecord(
const GlobalVariableTemplateRecord &Record) {}
diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
index c53435b6a79b90..ca12c8f7a9b013 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -188,6 +188,8 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
void visitMethodTemplateSpecializationRecord(
const CXXMethodTemplateSpecializationRecord &Record);
+ void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record);
+
void visitConceptRecord(const ConceptRecord &Record);
void
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index 103297ab143149..6cc00bc939c79c 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -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,
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 8a0a628d3f7354..36b0d1f07f1ba6 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -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);
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index 8a65679388c15f..6782483681a0b1 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -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)
diff --git a/clang/test/ExtractAPI/field_template.cpp b/clang/test/ExtractAPI/field_template.cpp
new file mode 100644
index 00000000000000..d746402eeb8de0
--- /dev/null
+++ b/clang/test/ExtractAPI/field_template.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
+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 at Foo@Bar",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ }
+ ],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Foo"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 7,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ]
+ },
+ {
+ "accessLevel": "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 at 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"
+ }
+ ]
+ }
+ }
+ ]
+}
More information about the cfe-commits
mailing list