From d8e9c5d9cab51f0ec21d4953014f41fe4dc603d9 Mon Sep 17 00:00:00 2001 From: Erick Velez Date: Mon, 21 Aug 2023 07:56:36 -0700 Subject: [PATCH] [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 --- clang/include/clang/ExtractAPI/API.h | 77 ++++ .../clang/ExtractAPI/ExtractAPIVisitor.h | 73 +++- .../ExtractAPI/Serialization/SerializerBase.h | 21 + .../Serialization/SymbolGraphSerializer.h | 5 + clang/lib/ExtractAPI/API.cpp | 33 ++ .../Serialization/SymbolGraphSerializer.cpp | 34 ++ clang/test/ExtractAPI/method_template.cpp | 244 ++++++++++++ .../test/ExtractAPI/method_template_spec.cpp | 371 ++++++++++++++++++ 8 files changed, 857 insertions(+), 1 deletion(-) create mode 100644 clang/test/ExtractAPI/method_template.cpp create mode 100644 clang/test/ExtractAPI/method_template_spec.cpp diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h index 7fc6447e93b9..cd20de6674f7 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 @@ 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 : public std::true_type {}; template <> struct has_function_signature : public std::true_type {}; +template <> +struct has_function_signature : public std::true_type { +}; +template <> +struct has_function_signature + : public std::true_type {}; template struct has_access : public std::false_type {}; template <> struct has_access : public std::true_type {}; template <> struct has_access : public std::true_type {}; +template <> +struct has_access : public std::true_type {}; +template <> +struct has_access + : public std::true_type {}; template struct has_template : public std::false_type {}; template <> struct has_template : public std::true_type {}; @@ -1074,6 +1125,8 @@ struct has_template : public std::true_type {}; template <> struct has_template : public std::true_type {}; +template <> +struct has_template : public std::true_type {}; template <> struct has_template : 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 &getEnums() const { return Enums; } const RecordMap &getStructs() const { return Structs; } const RecordMap &getCXXClasses() const { return CXXClasses; } + const RecordMap &getCXXMethodTemplates() const { + return CXXMethodTemplates; + } + const RecordMap & + getCXXMethodTemplateSpecializations() const { + return CXXMethodTemplateSpecializations; + } const RecordMap &getConcepts() const { return Concepts; } const RecordMap &getClassTemplates() const { return ClassTemplates; @@ -1487,6 +1561,9 @@ private: RecordMap Enums; RecordMap Structs; RecordMap CXXClasses; + RecordMap CXXMethodTemplates; + RecordMap + CXXMethodTemplateSpecializations; RecordMap ClassTemplates; RecordMap ClassTemplateSpecializations; RecordMap diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h index ec58dac59ae5..5dfffb3e8fd6 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 @@ -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::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::WalkUpFromCXXRecordDecl( return true; } +template +bool ExtractAPIVisitorBase::WalkUpFromCXXMethodDecl( + const CXXMethodDecl *Decl) { + getDerivedExtractAPIVisitor().VisitCXXMethodDecl(Decl); + return true; +} + template bool ExtractAPIVisitorBase::WalkUpFromClassTemplateSpecializationDecl( const ClassTemplateSpecializationDecl *Decl) { @@ -521,6 +533,60 @@ bool ExtractAPIVisitorBase::VisitCXXRecordDecl( return true; } +template +bool ExtractAPIVisitorBase::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(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 bool ExtractAPIVisitorBase::VisitConceptDecl(const ConceptDecl *Decl) { if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) @@ -712,6 +778,8 @@ bool ExtractAPIVisitorBase::VisitVarTemplatePartialSpecializationDecl( template bool ExtractAPIVisitorBase::VisitFunctionTemplateDecl( const FunctionTemplateDecl *Decl) { + if (isa(Decl->getTemplatedDecl())) + return true; if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) return true; @@ -1099,6 +1167,9 @@ void ExtractAPIVisitorBase::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 9a2140fcd031..dcf395f68d95 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -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) {} diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h index 77c844a32f20..c53435b6a79b 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -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 diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp index bc19630ae17e..103297ab1431 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 de6654b10089..8a65679388c1 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 000000000000..a6f27805d881 --- /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@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 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" + } + ] + } + } + ] +} diff --git a/clang/test/ExtractAPI/method_template_spec.cpp b/clang/test/ExtractAPI/method_template_spec.cpp new file mode 100644 index 000000000000..c3ba262d41f9 --- /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@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 void Bar(T Fizz); + + template<> void Bar(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" + ] + } + ] +}