[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
This commit is contained in:
Erick Velez 2023-08-21 07:56:36 -07:00
parent 1094e2ebba
commit d8e9c5d9ca
8 changed files with 857 additions and 1 deletions

View File

@ -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 @@ private:
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 @@ public:
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 @@ public:
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 @@ private:
RecordMap<EnumRecord> Enums;
RecordMap<StructRecord> Structs;
RecordMap<CXXClassRecord> CXXClasses;
RecordMap<CXXMethodTemplateRecord> CXXMethodTemplates;
RecordMap<CXXMethodTemplateSpecializationRecord>
CXXMethodTemplateSpecializations;
RecordMap<ClassTemplateRecord> ClassTemplates;
RecordMap<ClassTemplateSpecializationRecord> ClassTemplateSpecializations;
RecordMap<ClassTemplatePartialSpecializationRecord>

View File

@ -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 @@ public:
bool WalkUpFromCXXRecordDecl(const CXXRecordDecl *Decl);
bool WalkUpFromCXXMethodDecl(const CXXMethodDecl *Decl);
bool WalkUpFromClassTemplateSpecializationDecl(
const ClassTemplateSpecializationDecl *Decl);
@ -73,6 +76,8 @@ public:
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()) {

View File

@ -39,6 +39,10 @@ public:
getDerived()->traverseClassTemplatePartialSpecializationRecords();
getDerived()->traverseCXXMethodTemplates();
getDerived()->traverseCXXMethodTemplateSpecializations();
getDerived()->traverseConcepts();
getDerived()->traverseGlobalVariableTemplateRecords();
@ -94,6 +98,18 @@ public:
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 @@ public:
void visitClassTemplatePartialSpecializationRecord(
const ClassTemplatePartialSpecializationRecord &Record){};
void visitMethodTemplateRecord(const CXXMethodTemplateRecord &Record){};
void visitMethodTemplateSpecializationRecord(
const CXXMethodTemplateSpecializationRecord &Record){};
void visitGlobalVariableTemplateRecord(
const GlobalVariableTemplateRecord &Record) {}

View File

@ -183,6 +183,11 @@ public:
void visitClassTemplatePartialSpecializationRecord(
const ClassTemplatePartialSpecializationRecord &Record);
void visitMethodTemplateRecord(const CXXMethodTemplateRecord &Record);
void visitMethodTemplateSpecializationRecord(
const CXXMethodTemplateSpecializationRecord &Record);
void visitConceptRecord(const ConceptRecord &Record);
void

View File

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

View File

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

View File

@ -0,0 +1,244 @@
// 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> 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@Foo@FT@>1#TBar#t0.0#v#",
"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": "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@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"
}
]
}
}
]
}

View File

@ -0,0 +1,371 @@
// 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> 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@Foo@FT@>1#TBar#t0.0#v#",
"target": "c:@S@Foo",
"targetFallback": "Foo"
},
{
"kind": "memberOf",
"source": "c:@S@Foo@F@Bar<#I>#I#",
"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": "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@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@Foo@F@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"
]
}
]
}