[clang] d8e9c5d - [clang][ExtractAPI] Visit method templates with better scheme
Erick Velez via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 21 09:06:15 PDT 2023
Author: Erick Velez
Date: 2023-08-21T09:05:57-07:00
New Revision: d8e9c5d9cab51f0ec21d4953014f41fe4dc603d9
URL: https://github.com/llvm/llvm-project/commit/d8e9c5d9cab51f0ec21d4953014f41fe4dc603d9
DIFF: https://github.com/llvm/llvm-project/commit/d8e9c5d9cab51f0ec21d4953014f41fe4dc603d9.diff
LOG: [clang][ExtractAPI] Visit method templates with better scheme
Visit and serialize method templates and template specializations. Introduces a new scheme of visiting child Decls via VisitCXXMethodDecl which will be followed in future patches for Fields and non-template methods.
Depends on D157579
Reviewed By: dang
Differential Revision: https://reviews.llvm.org/D158027
Added:
clang/test/ExtractAPI/method_template.cpp
clang/test/ExtractAPI/method_template_spec.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/Serialization/SymbolGraphSerializer.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 7fc6447e93b961..cd20de6674f708 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -180,6 +180,8 @@ struct APIRecord {
RK_CXXInstanceMethod,
RK_CXXConstructorMethod,
RK_CXXDestructorMethod,
+ RK_CXXMethodTemplate,
+ RK_CXXMethodTemplateSpecialization,
RK_ObjCInstanceProperty,
RK_ObjCClassProperty,
RK_ObjCIvar,
@@ -623,6 +625,42 @@ struct CXXInstanceMethodRecord : CXXMethodRecord {
virtual void anchor();
};
+struct CXXMethodTemplateRecord : CXXMethodRecord {
+ Template Templ;
+
+ CXXMethodTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
+ AvailabilitySet Availabilities,
+ const DocComment &Comment,
+ DeclarationFragments Declaration,
+ DeclarationFragments SubHeading,
+ FunctionSignature Signature, AccessControl Access,
+ Template Template, bool IsFromSystemHeader)
+ : CXXMethodRecord(RK_CXXMethodTemplate, USR, Name, Loc,
+ std::move(Availabilities), Comment, Declaration,
+ SubHeading, Signature, Access, IsFromSystemHeader),
+ Templ(Template) {}
+
+ static bool classof(const APIRecord *Record) {
+ return Record->getKind() == RK_CXXMethodTemplate;
+ }
+};
+
+struct CXXMethodTemplateSpecializationRecord : CXXMethodRecord {
+ CXXMethodTemplateSpecializationRecord(
+ StringRef USR, StringRef Name, PresumedLoc Loc,
+ AvailabilitySet Availabilities, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ FunctionSignature Signature, AccessControl Access,
+ bool IsFromSystemHeader)
+ : CXXMethodRecord(RK_CXXMethodTemplateSpecialization, USR, Name, Loc,
+ std::move(Availabilities), Comment, Declaration,
+ SubHeading, Signature, Access, IsFromSystemHeader) {}
+
+ static bool classof(const APIRecord *Record) {
+ return Record->getKind() == RK_CXXMethodTemplateSpecialization;
+ }
+};
+
/// This holds information associated with Objective-C properties.
struct ObjCPropertyRecord : APIRecord {
/// The attributes associated with an Objective-C property.
@@ -794,6 +832,8 @@ struct SymbolReference {
: Name(Name), USR(USR), Source(Source) {}
SymbolReference(const APIRecord &Record)
: Name(Record.Name), USR(Record.USR) {}
+ SymbolReference(const APIRecord *Record)
+ : Name(Record->Name), USR(Record->USR) {}
/// Determine if this SymbolReference is empty.
///
@@ -1058,10 +1098,21 @@ template <>
struct has_function_signature<ObjCClassMethodRecord> : public std::true_type {};
template <>
struct has_function_signature<CXXMethodRecord> : public std::true_type {};
+template <>
+struct has_function_signature<CXXMethodTemplateRecord> : public std::true_type {
+};
+template <>
+struct has_function_signature<CXXMethodTemplateSpecializationRecord>
+ : public std::true_type {};
template <typename RecordTy> struct has_access : public std::false_type {};
template <> struct has_access<CXXMethodRecord> : public std::true_type {};
template <> struct has_access<CXXFieldRecord> : public std::true_type {};
+template <>
+struct has_access<CXXMethodTemplateRecord> : public std::true_type {};
+template <>
+struct has_access<CXXMethodTemplateSpecializationRecord>
+ : public std::true_type {};
template <typename RecordTy> struct has_template : public std::false_type {};
template <> struct has_template<ClassTemplateRecord> : public std::true_type {};
@@ -1074,6 +1125,8 @@ struct has_template<GlobalVariableTemplateRecord> : public std::true_type {};
template <>
struct has_template<GlobalVariableTemplatePartialSpecializationRecord>
: public std::true_type {};
+template <>
+struct has_template<CXXMethodTemplateRecord> : public std::true_type {};
template <>
struct has_template<GlobalFunctionTemplateRecord> : public std::true_type {};
@@ -1253,6 +1306,20 @@ class APISet {
FunctionSignature Signature, bool IsConstructor, AccessControl Access,
bool IsFromSystemHeader);
+ CXXMethodTemplateRecord *addCXXMethodTemplate(
+ APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ FunctionSignature Signature, AccessControl Access, Template Template,
+ bool IsFromSystemHeader);
+
+ CXXMethodTemplateSpecializationRecord *addCXXMethodTemplateSpec(
+ APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ FunctionSignature Signature, AccessControl Access,
+ bool IsFromSystemHeader);
+
ConceptRecord *addConcept(StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability,
const DocComment &Comment,
@@ -1408,6 +1475,13 @@ 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<CXXMethodTemplateRecord> &getCXXMethodTemplates() const {
+ return CXXMethodTemplates;
+ }
+ const RecordMap<CXXMethodTemplateSpecializationRecord> &
+ getCXXMethodTemplateSpecializations() const {
+ return CXXMethodTemplateSpecializations;
+ }
const RecordMap<ConceptRecord> &getConcepts() const { return Concepts; }
const RecordMap<ClassTemplateRecord> &getClassTemplates() const {
return ClassTemplates;
@@ -1487,6 +1561,9 @@ class APISet {
RecordMap<EnumRecord> Enums;
RecordMap<StructRecord> Structs;
RecordMap<CXXClassRecord> CXXClasses;
+ RecordMap<CXXMethodTemplateRecord> CXXMethodTemplates;
+ RecordMap<CXXMethodTemplateSpecializationRecord>
+ CXXMethodTemplateSpecializations;
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 ec58dac59ae59d..5dfffb3e8fd60a 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -27,6 +27,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/ExtractAPI/API.h"
#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h"
+#include "clang/Index/USRGeneration.h"
#include "llvm/ADT/StringRef.h"
#include <type_traits>
@@ -53,6 +54,8 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
bool WalkUpFromCXXRecordDecl(const CXXRecordDecl *Decl);
+ bool WalkUpFromCXXMethodDecl(const CXXMethodDecl *Decl);
+
bool WalkUpFromClassTemplateSpecializationDecl(
const ClassTemplateSpecializationDecl *Decl);
@@ -73,6 +76,8 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
bool VisitCXXRecordDecl(const CXXRecordDecl *Decl);
+ bool VisitCXXMethodDecl(const CXXMethodDecl *Decl);
+
bool VisitConceptDecl(const ConceptDecl *Decl);
bool VisitClassTemplateSpecializationDecl(
@@ -287,11 +292,11 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
switch (Decl->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
case FunctionDecl::TK_DependentNonTemplate:
- case FunctionDecl::TK_MemberSpecialization:
case FunctionDecl::TK_FunctionTemplateSpecialization:
break;
case FunctionDecl::TK_FunctionTemplate:
case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
+ case FunctionDecl::TK_MemberSpecialization:
return true;
}
@@ -387,6 +392,13 @@ bool ExtractAPIVisitorBase<Derived>::WalkUpFromCXXRecordDecl(
return true;
}
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::WalkUpFromCXXMethodDecl(
+ const CXXMethodDecl *Decl) {
+ getDerivedExtractAPIVisitor().VisitCXXMethodDecl(Decl);
+ return true;
+}
+
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::WalkUpFromClassTemplateSpecializationDecl(
const ClassTemplateSpecializationDecl *Decl) {
@@ -521,6 +533,60 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
return true;
}
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::VisitCXXMethodDecl(
+ const CXXMethodDecl *Decl) {
+ if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl) ||
+ Decl->isImplicit())
+ return true;
+ switch (Decl->getTemplatedKind()) {
+ case FunctionDecl::TK_MemberSpecialization:
+ case FunctionDecl::TK_FunctionTemplateSpecialization:
+ case FunctionDecl::TK_FunctionTemplate:
+ case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
+ break;
+ case FunctionDecl::TK_NonTemplate:
+ case FunctionDecl::TK_DependentNonTemplate:
+ 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 SubHeading =
+ DeclarationFragmentsBuilder::getSubHeading(Decl);
+
+ SmallString<128> ParentUSR;
+ index::generateUSRForDecl(dyn_cast<CXXRecordDecl>(Decl->getDeclContext()),
+ ParentUSR);
+ if (Decl->isTemplated()) {
+ FunctionTemplateDecl *TemplateDecl = Decl->getDescribedFunctionTemplate();
+ API.addCXXMethodTemplate(
+ API.findRecordForUSR(ParentUSR), Name, USR, Loc, AvailabilitySet(Decl),
+ Comment,
+ DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(
+ TemplateDecl),
+ SubHeading, DeclarationFragmentsBuilder::getFunctionSignature(Decl),
+ DeclarationFragmentsBuilder::getAccessControl(TemplateDecl),
+ Template(TemplateDecl), isInSystemHeader(Decl));
+ } else if (Decl->getTemplateSpecializationInfo())
+ API.addCXXMethodTemplateSpec(
+ API.findRecordForUSR(ParentUSR), Name, USR, Loc, AvailabilitySet(Decl),
+ Comment,
+ DeclarationFragmentsBuilder::
+ getFragmentsForFunctionTemplateSpecialization(Decl),
+ SubHeading, DeclarationFragmentsBuilder::getFunctionSignature(Decl),
+ DeclarationFragmentsBuilder::getAccessControl(Decl),
+ isInSystemHeader(Decl));
+ return true;
+}
+
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitConceptDecl(const ConceptDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
@@ -712,6 +778,8 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplatePartialSpecializationDecl(
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitFunctionTemplateDecl(
const FunctionTemplateDecl *Decl) {
+ if (isa<CXXMethodDecl>(Decl->getTemplatedDecl()))
+ return true;
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
return true;
@@ -1099,6 +1167,9 @@ void ExtractAPIVisitorBase<Derived>::recordCXXMethods(
continue;
}
+ if (Method->isFunctionTemplateSpecialization())
+ return;
+
StringRef Name;
DeclarationFragments Declaration;
if (Method->isOverloadedOperator()) {
diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
index 9a2140fcd0318f..dcf395f68d951a 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -39,6 +39,10 @@ template <typename Derived> class APISetVisitor {
getDerived()->traverseClassTemplatePartialSpecializationRecords();
+ getDerived()->traverseCXXMethodTemplates();
+
+ getDerived()->traverseCXXMethodTemplateSpecializations();
+
getDerived()->traverseConcepts();
getDerived()->traverseGlobalVariableTemplateRecords();
@@ -94,6 +98,18 @@ template <typename Derived> class APISetVisitor {
getDerived()->visitCXXClassRecord(*Class.second);
}
+ void traverseCXXMethodTemplates() {
+ for (const auto &MethodTemplate : API.getCXXMethodTemplates())
+ getDerived()->visitMethodTemplateRecord(*MethodTemplate.second);
+ }
+
+ void traverseCXXMethodTemplateSpecializations() {
+ for (const auto &MethodTemplateSpecialization :
+ API.getCXXMethodTemplateSpecializations())
+ getDerived()->visitMethodTemplateSpecializationRecord(
+ *MethodTemplateSpecialization.second);
+ }
+
void traverseClassTemplateRecords() {
for (const auto &ClassTemplate : API.getClassTemplates())
getDerived()->visitClassTemplateRecord(*ClassTemplate.second);
@@ -200,6 +216,11 @@ template <typename Derived> class APISetVisitor {
void visitClassTemplatePartialSpecializationRecord(
const ClassTemplatePartialSpecializationRecord &Record){};
+ void visitMethodTemplateRecord(const CXXMethodTemplateRecord &Record){};
+
+ void visitMethodTemplateSpecializationRecord(
+ const CXXMethodTemplateSpecializationRecord &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 77c844a32f20ec..c53435b6a79b90 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -183,6 +183,11 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
void visitClassTemplatePartialSpecializationRecord(
const ClassTemplatePartialSpecializationRecord &Record);
+ void visitMethodTemplateRecord(const CXXMethodTemplateRecord &Record);
+
+ void visitMethodTemplateSpecializationRecord(
+ const CXXMethodTemplateSpecializationRecord &Record);
+
void visitConceptRecord(const ConceptRecord &Record);
void
diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp
index bc19630ae17e1f..103297ab143149 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -311,6 +311,39 @@ CXXMethodRecord *APISet::addCXXSpecialMethod(
return CXXClassRecord->Methods.emplace_back(std::move(Record)).get();
}
+CXXMethodTemplateRecord *APISet::addCXXMethodTemplate(
+ APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ FunctionSignature Signature, AccessControl Access, Template Template,
+ bool IsFromSystemHeader) {
+ auto *Record = addTopLevelRecord(USRBasedLookupTable, CXXMethodTemplates, USR,
+ Name, Loc, std::move(Availability), Comment,
+ Declaration, SubHeading, Signature, Access,
+ Template, IsFromSystemHeader);
+ Record->ParentInformation = APIRecord::HierarchyInformation(
+ Parent->USR, Parent->Name, Parent->getKind(), Parent);
+
+ return Record;
+}
+
+CXXMethodTemplateSpecializationRecord *APISet::addCXXMethodTemplateSpec(
+ APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc,
+ AvailabilitySet Availability, const DocComment &Comment,
+ DeclarationFragments Declaration, DeclarationFragments SubHeading,
+ FunctionSignature Signature, AccessControl Access,
+ bool IsFromSystemHeader) {
+
+ auto *Record = addTopLevelRecord(
+ USRBasedLookupTable, CXXMethodTemplateSpecializations, USR, Name, Loc,
+ std::move(Availability), Comment, Declaration, SubHeading, Signature,
+ Access, IsFromSystemHeader);
+ Record->ParentInformation = APIRecord::HierarchyInformation(
+ Parent->USR, Parent->Name, Parent->getKind(), Parent);
+
+ return Record;
+}
+
ObjCCategoryRecord *APISet::addObjCCategory(
StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities, const DocComment &Comment,
diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
index de6654b1008940..8a65679388c15f 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -420,6 +420,14 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
Kind["identifier"] = AddLangPrefix("class");
Kind["displayName"] = "Class";
break;
+ case APIRecord::RK_CXXMethodTemplate:
+ Kind["identifier"] = AddLangPrefix("method");
+ Kind["displayName"] = "Method Template";
+ break;
+ case APIRecord::RK_CXXMethodTemplateSpecialization:
+ Kind["identifier"] = AddLangPrefix("method");
+ Kind["displayName"] = "Method Template Specialization";
+ break;
case APIRecord::RK_Concept:
Kind["identifier"] = AddLangPrefix("concept");
Kind["displayName"] = "Concept";
@@ -922,6 +930,32 @@ void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(
serializeRelationship(RelationshipKind::InheritsFrom, Record, Base);
}
+void SymbolGraphSerializer::visitMethodTemplateRecord(
+ const CXXMethodTemplateRecord &Record) {
+ if (!ShouldRecurse)
+ // Ignore child symbols
+ return;
+ auto MethodTemplate = serializeAPIRecord(Record);
+ if (!MethodTemplate)
+ return;
+ Symbols.emplace_back(std::move(*MethodTemplate));
+ serializeRelationship(RelationshipKind::MemberOf, Record,
+ Record.ParentInformation.ParentRecord);
+}
+
+void SymbolGraphSerializer::visitMethodTemplateSpecializationRecord(
+ const CXXMethodTemplateSpecializationRecord &Record) {
+ if (!ShouldRecurse)
+ // Ignore child symbols
+ return;
+ auto MethodTemplateSpecialization = serializeAPIRecord(Record);
+ if (!MethodTemplateSpecialization)
+ return;
+ Symbols.emplace_back(std::move(*MethodTemplateSpecialization));
+ 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/method_template.cpp b/clang/test/ExtractAPI/method_template.cpp
new file mode 100644
index 00000000000000..a6f27805d881ea
--- /dev/null
+++ b/clang/test/ExtractAPI/method_template.cpp
@@ -0,0 +1,244 @@
+// 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> void Bar(T Fizz);
+};
+
+/// 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@FT@>1#TBar#t0.0#v#",
+ "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": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Bar"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:t0.0",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "Fizz"
+ },
+ {
+ "kind": "text",
+ "spelling": ");"
+ }
+ ],
+ "functionSignature": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:t0.0",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "Fizz"
+ }
+ ],
+ "name": "Fizz"
+ }
+ ],
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Foo@FT@>1#TBar#t0.0#v#"
+ },
+ "kind": {
+ "displayName": "Method Template",
+ "identifier": "c++.method"
+ },
+ "location": {
+ "position": {
+ "character": 29,
+ "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"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/clang/test/ExtractAPI/method_template_spec.cpp b/clang/test/ExtractAPI/method_template_spec.cpp
new file mode 100644
index 00000000000000..c3ba262d41f9a1
--- /dev/null
+++ b/clang/test/ExtractAPI/method_template_spec.cpp
@@ -0,0 +1,371 @@
+// 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> void Bar(T Fizz);
+
+ template<> void Bar<int>(int Fizz);
+};
+
+/// 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@FT@>1#TBar#t0.0#v#",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ },
+ {
+ "kind": "memberOf",
+ "source": "c:@S at Foo@F at Bar<#I>#I#",
+ "target": "c:@S at Foo",
+ "targetFallback": "Foo"
+ }
+ ],
+ "symbols": [
+ {
+ "accessLevel": "public",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "class"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ },
+ {
+ "kind": "text",
+ "spelling": ";"
+ }
+ ],
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Foo"
+ },
+ "kind": {
+ "displayName": "Class",
+ "identifier": "c++.class"
+ },
+ "location": {
+ "position": {
+ "character": 7,
+ "line": 1
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Foo"
+ }
+ ],
+ "title": "Foo"
+ },
+ "pathComponents": [
+ "Foo"
+ ]
+ },
+ {
+ "accessLevel": "private",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "keyword",
+ "spelling": "typename"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "genericParameter",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": "> "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Bar"
+ },
+ {
+ "kind": "text",
+ "spelling": "("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:t0.0",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "Fizz"
+ },
+ {
+ "kind": "text",
+ "spelling": ");"
+ }
+ ],
+ "functionSignature": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:t0.0",
+ "spelling": "T"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "Fizz"
+ }
+ ],
+ "name": "Fizz"
+ }
+ ],
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Foo@FT@>1#TBar#t0.0#v#"
+ },
+ "kind": {
+ "displayName": "Method Template",
+ "identifier": "c++.method"
+ },
+ "location": {
+ "position": {
+ "character": 29,
+ "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"
+ }
+ ]
+ }
+ },
+ {
+ "accessLevel": "private",
+ "declarationFragments": [
+ {
+ "kind": "keyword",
+ "spelling": "template"
+ },
+ {
+ "kind": "text",
+ "spelling": "<> "
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "identifier",
+ "spelling": "Bar"
+ },
+ {
+ "kind": "text",
+ "spelling": "<"
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": ">("
+ },
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "Fizz"
+ },
+ {
+ "kind": "text",
+ "spelling": ");"
+ }
+ ],
+ "functionSignature": {
+ "parameters": [
+ {
+ "declarationFragments": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:I",
+ "spelling": "int"
+ },
+ {
+ "kind": "text",
+ "spelling": " "
+ },
+ {
+ "kind": "internalParam",
+ "spelling": "Fizz"
+ }
+ ],
+ "name": "Fizz"
+ }
+ ],
+ "returns": [
+ {
+ "kind": "typeIdentifier",
+ "preciseIdentifier": "c:v",
+ "spelling": "void"
+ }
+ ]
+ },
+ "identifier": {
+ "interfaceLanguage": "c++",
+ "precise": "c:@S at Foo@F at Bar<#I>#I#"
+ },
+ "kind": {
+ "displayName": "Method Template Specialization",
+ "identifier": "c++.method"
+ },
+ "location": {
+ "position": {
+ "character": 19,
+ "line": 4
+ },
+ "uri": "file://INPUT_DIR/input.h"
+ },
+ "names": {
+ "navigator": [
+ {
+ "kind": "identifier",
+ "spelling": "Bar"
+ }
+ ],
+ "subHeading": [
+ {
+ "kind": "identifier",
+ "spelling": "Bar"
+ }
+ ],
+ "title": "Bar"
+ },
+ "pathComponents": [
+ "Foo",
+ "Bar"
+ ]
+ }
+ ]
+}
More information about the cfe-commits
mailing list