diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -171,6 +171,7 @@ RK_Union, RK_StaticField, RK_CXXField, + RK_CXXFieldTemplate, RK_CXXClass, RK_ClassTemplate, RK_ClassTemplateSpecialization, @@ -530,6 +531,25 @@ virtual void anchor(); }; +struct CXXFieldTemplateRecord : CXXFieldRecord { + Template Templ; + + CXXFieldTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, AccessControl Access, + Template Template, bool IsFromSystemHeader) + : CXXFieldRecord(RK_CXXFieldTemplate, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Access, IsFromSystemHeader), + Templ(Template) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_CXXFieldTemplate; + } +}; + struct CXXMethodRecord : APIRecord { FunctionSignature Signature; AccessControl Access; @@ -1113,6 +1133,8 @@ 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 {}; @@ -1127,6 +1149,8 @@ : 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 {}; @@ -1251,6 +1275,12 @@ DeclarationFragments SubHeading, AccessControl Access, bool IsFromSystemHeader); + CXXFieldTemplateRecord *addCXXFieldTemplate( + APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + AccessControl Access, Template Template, bool IsFromSystemHeader); + CXXClassRecord * addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, @@ -1482,6 +1512,9 @@ getCXXMethodTemplateSpecializations() const { return CXXMethodTemplateSpecializations; } + const RecordMap &getCXXFieldTemplates() const { + return CXXFieldTemplates; + } const RecordMap &getConcepts() const { return Concepts; } const RecordMap &getClassTemplates() const { return ClassTemplates; @@ -1564,6 +1597,7 @@ RecordMap CXXMethodTemplates; RecordMap CXXMethodTemplateSpecializations; + RecordMap CXXFieldTemplates; RecordMap ClassTemplates; RecordMap ClassTemplateSpecializations; RecordMap diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -697,20 +697,29 @@ Context.getDiagnostics()); // Build declaration fragments and sub-heading for the variable. - DeclarationFragments Declaration = - DeclarationFragmentsBuilder::getFragmentsForVarTemplate( - Decl->getTemplatedDecl()); + DeclarationFragments Declaration; + Declaration + .append(DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate( + Decl)) + .append(DeclarationFragmentsBuilder::getFragmentsForVarTemplate( + Decl->getTemplatedDecl())); + // Inject template fragments before var fragments. DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); - // Inject template fragments before var fragments. - Declaration.insert( - Declaration.begin(), - DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(Decl)); - - API.addGlobalVariableTemplate(Name, USR, Loc, AvailabilitySet(Decl), Linkage, - Comment, Declaration, SubHeading, - Template(Decl), isInSystemHeader(Decl)); + SmallString<128> ParentUSR; + index::generateUSRForDecl(dyn_cast(Decl->getDeclContext()), + ParentUSR); + if (Decl->getDeclContext()->getDeclKind() == Decl::CXXRecord) + API.addCXXFieldTemplate(API.findRecordForUSR(ParentUSR), Name, USR, Loc, + AvailabilitySet(Decl), Comment, Declaration, + SubHeading, + DeclarationFragmentsBuilder::getAccessControl(Decl), + Template(Decl), isInSystemHeader(Decl)); + else + API.addGlobalVariableTemplate(Name, USR, Loc, AvailabilitySet(Decl), + Linkage, Comment, Declaration, SubHeading, + Template(Decl), isInSystemHeader(Decl)); return true; } diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -43,6 +43,8 @@ getDerived()->traverseCXXMethodTemplateSpecializations(); + getDerived()->traverseCXXFieldTemplates(); + getDerived()->traverseConcepts(); getDerived()->traverseGlobalVariableTemplateRecords(); @@ -129,6 +131,11 @@ *ClassTemplatePartialSpecialization.second); } + void traverseCXXFieldTemplates() { + for (const auto &CXXFieldTemplate : API.getCXXFieldTemplates()) + getDerived()->visitCXXFieldTemplateRecord(*CXXFieldTemplate.second); + } + void traverseGlobalVariableTemplateRecords() { for (const auto &GlobalVariableTemplate : API.getGlobalVariableTemplates()) getDerived()->visitGlobalVariableTemplateRecord( @@ -221,6 +228,8 @@ void visitMethodTemplateSpecializationRecord( const CXXMethodTemplateSpecializationRecord &Record){}; + void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record){}; + void visitGlobalVariableTemplateRecord( const GlobalVariableTemplateRecord &Record) {} diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -188,6 +188,8 @@ void visitMethodTemplateSpecializationRecord( const CXXMethodTemplateSpecializationRecord &Record); + void visitCXXFieldTemplateRecord(const CXXFieldTemplateRecord &Record); + void visitConceptRecord(const ConceptRecord &Record); void diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -185,6 +185,21 @@ return CXXClass->Fields.emplace_back(std::move(Record)).get(); } +CXXFieldTemplateRecord *APISet::addCXXFieldTemplate( + APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + AccessControl Access, Template Template, bool IsFromSystemHeader) { + auto *Record = + addTopLevelRecord(USRBasedLookupTable, CXXFieldTemplates, USR, Name, Loc, + std::move(Availability), Comment, Declaration, + SubHeading, Access, Template, IsFromSystemHeader); + Record->ParentInformation = APIRecord::HierarchyInformation( + Parent->USR, Parent->Name, Parent->getKind(), Parent); + + return Record; +} + CXXClassRecord * APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -465,6 +465,11 @@ ? Var->getTypeSourceInfo()->getType() : Var->getASTContext().getUnqualifiedObjCPointerType(Var->getType()); + // Might be a member, so might be static. + if (Var->isStaticDataMember()) + Fragments.append("static", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + DeclarationFragments After; DeclarationFragments ArgumentFragment = getFragmentsForType(T, Var->getASTContext(), After); diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -428,6 +428,10 @@ Kind["identifier"] = AddLangPrefix("method"); Kind["displayName"] = "Method Template Specialization"; break; + case APIRecord::RK_CXXFieldTemplate: + Kind["identifier"] = AddLangPrefix("property"); + Kind["displayName"] = "Template Property"; + break; case APIRecord::RK_Concept: Kind["identifier"] = AddLangPrefix("concept"); Kind["displayName"] = "Concept"; @@ -956,6 +960,19 @@ Record.ParentInformation.ParentRecord); } +void SymbolGraphSerializer::visitCXXFieldTemplateRecord( + const CXXFieldTemplateRecord &Record) { + if (!ShouldRecurse) + // Ignore child symbols + return; + auto CXXFieldTemplate = serializeAPIRecord(Record); + if (!CXXFieldTemplate) + return; + Symbols.emplace_back(std::move(*CXXFieldTemplate)); + serializeRelationship(RelationshipKind::MemberOf, Record, + Record.ParentInformation.ParentRecord); +} + void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) { auto Concept = serializeAPIRecord(Record); if (!Concept) diff --git a/clang/test/ExtractAPI/field_template.cpp b/clang/test/ExtractAPI/field_template.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/field_template.cpp @@ -0,0 +1,206 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@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 static T Bar; +}; + +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@S@Foo@Bar", + "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": "keyword", + "spelling": "static" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:t0.0", + "spelling": "T" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Bar" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@Bar" + }, + "kind": { + "displayName": "Template Property", + "identifier": "c++.property" + }, + "location": { + "position": { + "character": 33, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "title": "Bar" + }, + "pathComponents": [ + "Foo", + "Bar" + ], + "swiftGenerics": { + "parameters": [ + { + "depth": 0, + "index": 0, + "name": "T" + } + ] + } + } + ] +}