[clang][ExtractAPI] Add support for C++ global function templates

Add records, serialization for global function templates and their specializations

Depends on D157350

Reviewed By: dang

Differential Revision: https://reviews.llvm.org/D157579
This commit is contained in:
Erick Velez 2023-08-18 14:33:51 -07:00
parent 3e569883fa
commit 80b787e803
10 changed files with 920 additions and 22 deletions

View File

@ -158,6 +158,8 @@ struct APIRecord {
enum RecordKind {
RK_Unknown,
RK_GlobalFunction,
RK_GlobalFunctionTemplate,
RK_GlobalFunctionTemplateSpecialization,
RK_GlobalVariable,
RK_GlobalVariableTemplate,
RK_GlobalVariableTemplateSpecialization,
@ -281,6 +283,16 @@ struct GlobalFunctionRecord : APIRecord {
IsFromSystemHeader),
Signature(Signature) {}
GlobalFunctionRecord(RecordKind Kind, StringRef USR, StringRef Name,
PresumedLoc Loc, AvailabilitySet Availabilities,
LinkageInfo Linkage, const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading,
FunctionSignature Signature, bool IsFromSystemHeader)
: APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), Linkage,
Comment, Declaration, SubHeading, IsFromSystemHeader),
Signature(Signature) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_GlobalFunction;
}
@ -289,6 +301,44 @@ private:
virtual void anchor();
};
struct GlobalFunctionTemplateRecord : GlobalFunctionRecord {
Template Templ;
GlobalFunctionTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities,
LinkageInfo Linkage, const DocComment &Comment,
DeclarationFragments Declaration,
DeclarationFragments SubHeading,
FunctionSignature Signature, Template Template,
bool IsFromSystemHeader)
: GlobalFunctionRecord(RK_GlobalFunctionTemplate, USR, Name, Loc,
std::move(Availabilities), Linkage, Comment,
Declaration, SubHeading, Signature,
IsFromSystemHeader),
Templ(Template) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_GlobalFunctionTemplate;
}
};
struct GlobalFunctionTemplateSpecializationRecord : GlobalFunctionRecord {
GlobalFunctionTemplateSpecializationRecord(
StringRef USR, StringRef Name, PresumedLoc Loc,
AvailabilitySet Availabilities, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature,
bool IsFromSystemHeader)
: GlobalFunctionRecord(RK_GlobalFunctionTemplateSpecialization, USR, Name,
Loc, std::move(Availabilities), Linkage, Comment,
Declaration, SubHeading, Signature,
IsFromSystemHeader) {}
static bool classof(const APIRecord *Record) {
return Record->getKind() == RK_GlobalFunctionTemplateSpecialization;
}
};
/// This holds information associated with global functions.
struct GlobalVariableRecord : APIRecord {
GlobalVariableRecord(StringRef USR, StringRef Name, PresumedLoc Loc,
@ -1025,6 +1075,15 @@ template <>
struct has_template<GlobalVariableTemplatePartialSpecializationRecord>
: public std::true_type {};
template <>
struct has_template<GlobalFunctionTemplateRecord> : public std::true_type {};
template <>
struct has_function_signature<GlobalFunctionTemplateRecord>
: public std::true_type {};
template <>
struct has_function_signature<GlobalFunctionTemplateSpecializationRecord>
: public std::true_type {};
/// APISet holds the set of API records collected from given inputs.
class APISet {
public:
@ -1061,6 +1120,21 @@ public:
DeclarationFragments SubHeading,
FunctionSignature Signature, bool IsFromSystemHeader);
GlobalFunctionTemplateRecord *addGlobalFunctionTemplate(
StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature,
Template Template, bool IsFromSystemHeader);
GlobalFunctionTemplateSpecializationRecord *
addGlobalFunctionTemplateSpecialization(
StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature,
bool IsFromSystemHeader);
/// Create and add an enum constant record into the API set.
///
/// Note: the caller is responsible for keeping the StringRef \p Name and
@ -1305,6 +1379,14 @@ public:
const RecordMap<GlobalFunctionRecord> &getGlobalFunctions() const {
return GlobalFunctions;
}
const RecordMap<GlobalFunctionTemplateRecord> &
getGlobalFunctionTemplates() const {
return GlobalFunctionTemplates;
}
const RecordMap<GlobalFunctionTemplateSpecializationRecord> &
getGlobalFunctionTemplateSpecializations() const {
return GlobalFunctionTemplateSpecializations;
}
const RecordMap<GlobalVariableRecord> &getGlobalVariables() const {
return GlobalVariables;
}
@ -1391,6 +1473,9 @@ private:
llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
RecordMap<GlobalFunctionRecord> GlobalFunctions;
RecordMap<GlobalFunctionTemplateRecord> GlobalFunctionTemplates;
RecordMap<GlobalFunctionTemplateSpecializationRecord>
GlobalFunctionTemplateSpecializations;
RecordMap<GlobalVariableRecord> GlobalVariables;
RecordMap<GlobalVariableTemplateRecord> GlobalVariableTemplates;
RecordMap<GlobalVariableTemplateSpecializationRecord>

View File

@ -306,8 +306,8 @@ public:
static DeclarationFragments
getFragmentsForTemplateParameters(ArrayRef<NamedDecl *>);
static std::string getNameForTemplateArgument(const ArrayRef<NamedDecl *>,
std::string);
static std::string
getNameForTemplateArgument(const ArrayRef<NamedDecl *>, std::string);
static DeclarationFragments
getFragmentsForTemplateArguments(const ArrayRef<TemplateArgument>,
@ -331,6 +331,12 @@ public:
static DeclarationFragments getFragmentsForVarTemplatePartialSpecialization(
const VarTemplatePartialSpecializationDecl *);
static DeclarationFragments
getFragmentsForFunctionTemplate(const FunctionTemplateDecl *Decl);
static DeclarationFragments
getFragmentsForFunctionTemplateSpecialization(const FunctionDecl *Decl);
/// Build DeclarationFragments for an Objective-C category declaration
/// ObjCCategoryDecl.
static DeclarationFragments
@ -405,10 +411,21 @@ DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) {
FunctionSignature Signature;
DeclarationFragments ReturnType, After;
ReturnType
.append(getFragmentsForType(Function->getReturnType(),
Function->getASTContext(), After))
.append(std::move(After));
ReturnType = getFragmentsForType(Function->getReturnType(),
Function->getASTContext(), After);
if (isa<FunctionDecl>(Function) &&
dyn_cast<FunctionDecl>(Function)->getDescribedFunctionTemplate() &&
ReturnType.begin()->Spelling.substr(0, 14).compare("type-parameter") ==
0) {
std::string ProperArgName =
getNameForTemplateArgument(dyn_cast<FunctionDecl>(Function)
->getDescribedFunctionTemplate()
->getTemplateParameters()
->asArray(),
ReturnType.begin()->Spelling);
ReturnType.begin()->Spelling.swap(ProperArgName);
}
ReturnType.append(std::move(After));
Signature.setReturnType(ReturnType);
for (const auto *Param : Function->parameters())

View File

@ -67,6 +67,8 @@ public:
bool WalkUpFromVarTemplatePartialSpecializationDecl(
const VarTemplatePartialSpecializationDecl *Decl);
bool WalkUpFromFunctionTemplateDecl(const FunctionTemplateDecl *Decl);
bool VisitRecordDecl(const RecordDecl *Decl);
bool VisitCXXRecordDecl(const CXXRecordDecl *Decl);
@ -87,6 +89,8 @@ public:
bool VisitVarTemplatePartialSpecializationDecl(
const VarTemplatePartialSpecializationDecl *Decl);
bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *Decl);
bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl);
bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl);
@ -283,13 +287,8 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
switch (Decl->getTemplatedKind()) {
case FunctionDecl::TK_NonTemplate:
case FunctionDecl::TK_DependentNonTemplate:
break;
case FunctionDecl::TK_MemberSpecialization:
case FunctionDecl::TK_FunctionTemplateSpecialization:
if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
return true;
}
break;
case FunctionDecl::TK_FunctionTemplate:
case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
@ -312,17 +311,23 @@ bool ExtractAPIVisitorBase<Derived>::VisitFunctionDecl(
Context.getDiagnostics());
// Build declaration fragments, sub-heading, and signature of the function.
DeclarationFragments Declaration =
DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);
FunctionSignature Signature =
DeclarationFragmentsBuilder::getFunctionSignature(Decl);
// Add the function record to the API set.
API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
Declaration, SubHeading, Signature,
isInSystemHeader(Decl));
if (Decl->getTemplateSpecializationInfo())
API.addGlobalFunctionTemplateSpecialization(
Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
DeclarationFragmentsBuilder::
getFragmentsForFunctionTemplateSpecialization(Decl),
SubHeading, Signature, isInSystemHeader(Decl));
else
// Add the function record to the API set.
API.addGlobalFunction(
Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
DeclarationFragmentsBuilder::getFragmentsForFunction(Decl), SubHeading,
Signature, isInSystemHeader(Decl));
return true;
}
@ -420,6 +425,13 @@ bool ExtractAPIVisitorBase<Derived>::
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::WalkUpFromFunctionTemplateDecl(
const FunctionTemplateDecl *Decl) {
getDerivedExtractAPIVisitor().VisitFunctionTemplateDecl(Decl);
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
@ -697,6 +709,38 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarTemplatePartialSpecializationDecl(
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitFunctionTemplateDecl(
const FunctionTemplateDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
return true;
// Collect symbol information.
StringRef Name = Decl->getName();
StringRef USR = API.recordUSR(Decl);
PresumedLoc Loc =
Context.getSourceManager().getPresumedLoc(Decl->getLocation());
LinkageInfo Linkage = Decl->getLinkageAndVisibility();
DocComment Comment;
if (auto *RawComment =
getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl))
Comment = RawComment->getFormattedLines(Context.getSourceManager(),
Context.getDiagnostics());
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);
FunctionSignature Signature =
DeclarationFragmentsBuilder::getFunctionSignature(
Decl->getTemplatedDecl());
API.addGlobalFunctionTemplate(
Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment,
DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(Decl),
SubHeading, Signature, Template(Decl), isInSystemHeader(Decl));
return true;
}
template <typename Derived>
bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl(
const ObjCInterfaceDecl *Decl) {

View File

@ -47,6 +47,10 @@ public:
getDerived()->traverseGlobalVariableTemplatePartialSpecializationRecords();
getDerived()->traverseGlobalFunctionTemplateRecords();
getDerived()->traverseGlobalFunctionTemplateSpecializationRecords();
getDerived()->traverseStructRecords();
getDerived()->traverseObjCInterfaces();
@ -129,6 +133,19 @@ public:
*GlobalVariableTemplatePartialSpecialization.second);
}
void traverseGlobalFunctionTemplateRecords() {
for (const auto &GlobalFunctionTemplate : API.getGlobalFunctionTemplates())
getDerived()->visitGlobalFunctionTemplateRecord(
*GlobalFunctionTemplate.second);
}
void traverseGlobalFunctionTemplateSpecializationRecords() {
for (const auto &GlobalFunctionTemplateSpecialization :
API.getGlobalFunctionTemplateSpecializations())
getDerived()->visitGlobalFunctionTemplateSpecializationRecord(
*GlobalFunctionTemplateSpecialization.second);
}
void traverseConcepts() {
for (const auto &Concept : API.getConcepts())
getDerived()->visitConceptRecord(*Concept.second);
@ -192,6 +209,12 @@ public:
void visitGlobalVariableTemplatePartialSpecializationRecord(
const GlobalVariableTemplatePartialSpecializationRecord &Record){};
void visitGlobalFunctionTemplateRecord(
const GlobalFunctionTemplateRecord &Record){};
void visitGlobalFunctionTemplateSpecializationRecord(
const GlobalFunctionTemplateSpecializationRecord &Record){};
/// Visit an Objective-C container record.
void visitObjCContainerRecord(const ObjCContainerRecord &Record){};

View File

@ -194,6 +194,12 @@ public:
void visitGlobalVariableTemplatePartialSpecializationRecord(
const GlobalVariableTemplatePartialSpecializationRecord &Record);
void
visitGlobalFunctionTemplateRecord(const GlobalFunctionTemplateRecord &Record);
void visitGlobalFunctionTemplateSpecializationRecord(
const GlobalFunctionTemplateSpecializationRecord &Record);
/// Visit an Objective-C container record.
void visitObjCContainerRecord(const ObjCContainerRecord &Record);

View File

@ -78,6 +78,31 @@ GlobalFunctionRecord *APISet::addGlobalFunction(
IsFromSystemHeader);
}
GlobalFunctionTemplateRecord *APISet::addGlobalFunctionTemplate(
StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature,
Template Template, bool IsFromSystemHeader) {
return addTopLevelRecord(USRBasedLookupTable, GlobalFunctionTemplates, USR,
Name, Loc, std::move(Availability), Linkage, Comment,
Declaration, SubHeading, Signature, Template,
IsFromSystemHeader);
}
GlobalFunctionTemplateSpecializationRecord *
APISet::addGlobalFunctionTemplateSpecialization(
StringRef Name, StringRef USR, PresumedLoc Loc,
AvailabilitySet Availability, LinkageInfo Linkage,
const DocComment &Comment, DeclarationFragments Declaration,
DeclarationFragments SubHeading, FunctionSignature Signature,
bool IsFromSystemHeader) {
return addTopLevelRecord(
USRBasedLookupTable, GlobalFunctionTemplateSpecializations, USR, Name,
Loc, std::move(Availability), Linkage, Comment, Declaration, SubHeading,
Signature, IsFromSystemHeader);
}
EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name,
StringRef USR, PresumedLoc Loc,
AvailabilitySet Availabilities,

View File

@ -493,6 +493,16 @@ DeclarationFragmentsBuilder::getFragmentsForParam(const ParmVarDecl *Param) {
DeclarationFragments TypeFragments =
getFragmentsForType(T, Param->getASTContext(), After);
if (TypeFragments.begin()->Spelling.substr(0, 14).compare("type-parameter") ==
0) {
std::string ProperArgName = getNameForTemplateArgument(
dyn_cast<FunctionDecl>(Param->getDeclContext())
->getDescribedFunctionTemplate()
->getTemplateParameters()
->asArray(),
TypeFragments.begin()->Spelling);
TypeFragments.begin()->Spelling.swap(ProperArgName);
}
if (Param->isObjCMethodParameter())
Fragments.append("(", DeclarationFragments::FragmentKind::Text)
@ -536,12 +546,35 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) {
// FIXME: Is `after` actually needed here?
DeclarationFragments After;
Fragments
.append(getFragmentsForType(Func->getReturnType(), Func->getASTContext(),
After))
auto ReturnValueFragment =
getFragmentsForType(Func->getReturnType(), Func->getASTContext(), After);
if (ReturnValueFragment.begin()->Spelling.substr(0, 14).compare(
"type-parameter") == 0) {
std::string ProperArgName =
getNameForTemplateArgument(Func->getDescribedFunctionTemplate()
->getTemplateParameters()
->asArray(),
ReturnValueFragment.begin()->Spelling);
ReturnValueFragment.begin()->Spelling.swap(ProperArgName);
}
Fragments.append(std::move(ReturnValueFragment))
.appendSpace()
.append(Func->getName(), DeclarationFragments::FragmentKind::Identifier)
.append(std::move(After));
.append(Func->getName(), DeclarationFragments::FragmentKind::Identifier);
if (Func->getTemplateSpecializationInfo()) {
Fragments.append("<", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Func->getNumParams(); i != end; ++i) {
if (i)
Fragments.append(", ", DeclarationFragments::FragmentKind::Text);
Fragments.append(
getFragmentsForType(Func->getParamDecl(i)->getType(),
Func->getParamDecl(i)->getASTContext(), After));
}
Fragments.append(">", DeclarationFragments::FragmentKind::Text);
}
Fragments.append(std::move(After));
Fragments.append("(", DeclarationFragments::FragmentKind::Text);
for (unsigned i = 0, end = Func->getNumParams(); i != end; ++i) {
@ -974,6 +1007,33 @@ DeclarationFragmentsBuilder::getFragmentsForVarTemplatePartialSpecialization(
.append(";", DeclarationFragments::FragmentKind::Text);
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForFunctionTemplate(
const FunctionTemplateDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.append("<", DeclarationFragments::FragmentKind::Text)
// Partial specs may have new params.
.append(getFragmentsForTemplateParameters(
Decl->getTemplateParameters()->asArray()))
.append(">", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForFunction(
Decl->getAsFunction()));
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForFunctionTemplateSpecialization(
const FunctionDecl *Decl) {
DeclarationFragments Fragments;
return Fragments
.append("template", DeclarationFragments::FragmentKind::Keyword)
.append("<>", DeclarationFragments::FragmentKind::Text)
.appendSpace()
.append(DeclarationFragmentsBuilder::getFragmentsForFunction(Decl));
}
DeclarationFragments
DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name,
const MacroDirective *MD) {

View File

@ -361,6 +361,14 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
Kind["identifier"] = AddLangPrefix("func");
Kind["displayName"] = "Function";
break;
case APIRecord::RK_GlobalFunctionTemplate:
Kind["identifier"] = AddLangPrefix("func");
Kind["displayName"] = "Function Template";
break;
case APIRecord::RK_GlobalFunctionTemplateSpecialization:
Kind["identifier"] = AddLangPrefix("func");
Kind["displayName"] = "Function Template Specialization";
break;
case APIRecord::RK_GlobalVariableTemplate:
Kind["identifier"] = AddLangPrefix("var");
Kind["displayName"] = "Global Variable Template";
@ -947,6 +955,22 @@ void SymbolGraphSerializer::
Symbols.emplace_back(std::move(*GlobalVariableTemplatePartialSpecialization));
}
void SymbolGraphSerializer::visitGlobalFunctionTemplateRecord(
const GlobalFunctionTemplateRecord &Record) {
auto GlobalFunctionTemplate = serializeAPIRecord(Record);
if (!GlobalFunctionTemplate)
return;
Symbols.emplace_back(std::move(*GlobalFunctionTemplate));
}
void SymbolGraphSerializer::visitGlobalFunctionTemplateSpecializationRecord(
const GlobalFunctionTemplateSpecializationRecord &Record) {
auto GlobalFunctionTemplateSpecialization = serializeAPIRecord(Record);
if (!GlobalFunctionTemplateSpecialization)
return;
Symbols.emplace_back(std::move(*GlobalFunctionTemplateSpecialization));
}
void SymbolGraphSerializer::visitObjCContainerRecord(
const ObjCContainerRecord &Record) {
auto ObjCContainer = serializeAPIRecord(Record);

View File

@ -0,0 +1,315 @@
// 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> void Foo(T Bar);
template<typename T> T Fizz(int Buzz);
/// 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": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:t0.0",
"spelling": "T"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "Bar"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:t0.0",
"spelling": "T"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "Bar"
}
],
"name": "Bar"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@FT@>1#TFoo#t0.0#v#"
},
"kind": {
"displayName": "Function Template",
"identifier": "c++.func"
},
"location": {
"position": {
"character": 27,
"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": "typename"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "genericParameter",
"spelling": "T"
},
{
"kind": "text",
"spelling": "> "
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:t0.0",
"spelling": "T"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Fizz"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "Buzz"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "Buzz"
}
],
"name": "Buzz"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:t0.0",
"spelling": "T"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@FT@>1#TFizz#I#t0.0#"
},
"kind": {
"displayName": "Function Template",
"identifier": "c++.func"
},
"location": {
"position": {
"character": 24,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Fizz"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Fizz"
}
],
"title": "Fizz"
},
"pathComponents": [
"Fizz"
],
"swiftGenerics": {
"parameters": [
{
"depth": 0,
"index": 0,
"name": "T"
}
]
}
}
]
}

View File

@ -0,0 +1,299 @@
// 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> void Foo(T Bar);
template<> void Foo<int>(int 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": [],
"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": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:t0.0",
"spelling": "T"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "Bar"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:t0.0",
"spelling": "T"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "Bar"
}
],
"name": "Bar"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@FT@>1#TFoo#t0.0#v#"
},
"kind": {
"displayName": "Function Template",
"identifier": "c++.func"
},
"location": {
"position": {
"character": 27,
"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": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "Foo"
},
{
"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": "Bar"
},
{
"kind": "text",
"spelling": ");"
}
],
"functionSignature": {
"parameters": [
{
"declarationFragments": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:I",
"spelling": "int"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "Bar"
}
],
"name": "Bar"
}
],
"returns": [
{
"kind": "typeIdentifier",
"preciseIdentifier": "c:v",
"spelling": "void"
}
]
},
"identifier": {
"interfaceLanguage": "c++",
"precise": "c:@F@Foo<#I>#I#"
},
"kind": {
"displayName": "Function Template Specialization",
"identifier": "c++.func"
},
"location": {
"position": {
"character": 17,
"line": 3
},
"uri": "file://INPUT_DIR/input.h"
},
"names": {
"navigator": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"subHeading": [
{
"kind": "identifier",
"spelling": "Foo"
}
],
"title": "Foo"
},
"pathComponents": [
"Foo"
]
}
]
}