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 @@ -69,6 +69,10 @@ RK_StaticField, RK_CXXField, RK_CXXClass, + RK_ClassTemplate, + RK_ClassTemplateSpecialization, + RK_ClassTemplatePartialSpecialization, + RK_Concept, RK_CXXStaticMethod, RK_CXXInstanceMethod, RK_CXXConstructorMethod, @@ -640,6 +644,75 @@ virtual void anchor(); }; +struct ClassTemplateRecord : CXXClassRecord { + Template Templ; + + ClassTemplateRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, Template Template, + bool IsFromSystemHeader) + : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment, + Declaration, SubHeading, RK_ClassTemplate, + IsFromSystemHeader), + Templ(Template) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_ClassTemplate; + } +}; + +struct ClassTemplateSpecRecord : CXXClassRecord { + ClassTemplateSpecRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + bool IsFromSystemHeader) + : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment, + Declaration, SubHeading, RK_ClassTemplateSpecialization, + IsFromSystemHeader) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_ClassTemplateSpecialization; + } +}; + +struct ClassTemplatePartialSpecRecord : CXXClassRecord { + Template Templ; + ClassTemplatePartialSpecRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + Template Template, bool IsFromSystemHeader) + : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment, + Declaration, SubHeading, RK_ClassTemplateSpecialization, + IsFromSystemHeader), + Templ(Template) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_ClassTemplatePartialSpecialization; + } +}; + +struct ConceptRecord : APIRecord { + Template Templ; + ConceptRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, Template Template, + bool IsFromSystemHeader) + : APIRecord(RK_Concept, USR, Name, Loc, std::move(Availabilities), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader), + Templ(Template) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_Concept; + } +}; + /// This holds information associated with Objective-C categories. struct ObjCCategoryRecord : ObjCContainerRecord { SymbolReference Interface; @@ -773,6 +846,12 @@ 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 {}; +template <> +struct has_template : public std::true_type {}; +template <> struct has_template : public std::true_type {}; + /// APISet holds the set of API records collected from given inputs. class APISet { public: @@ -870,6 +949,26 @@ DeclarationFragments Declaration, DeclarationFragments SubHeading, APIRecord::RecordKind Kind, bool IsFromSystemHeader); + ClassTemplateRecord * + addClassTemplate(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, Template Template, + bool IsFromSystemHeader); + + ClassTemplateSpecRecord * + addClassTemplateSpec(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + bool IsFromSystemHeader); + + ClassTemplatePartialSpecRecord *addClassTemplatePartialSpec( + StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + Template Template, bool IsFromSystemHeader); + CXXMethodRecord * addCXXMethod(CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, @@ -884,6 +983,13 @@ FunctionSignature Signature, bool IsConstructor, AccessControl Access, bool IsFromSystemHeader); + ConceptRecord *addConcept(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, Template Template, + bool IsFromSystemHeader); + /// Create and add an Objective-C category record into the API set. /// /// Note: the caller is responsible for keeping the StringRef \p Name and @@ -1012,6 +1118,19 @@ const RecordMap &getEnums() const { return Enums; } const RecordMap &getStructs() const { return Structs; } const RecordMap &getCXXClasses() const { return CXXClasses; } + const RecordMap &getConcepts() const { return Concepts; } + const RecordMap &getClassTemplates() const { + return ClassTemplates; + } + const RecordMap & + getClassTemplateSpecializations() const { + return ClassTemplateSpecializations; + } + const RecordMap & + getClassTemplatePartialSpecializations() const { + return ClassTemplatePartialSpecializations; + } + const RecordMap &getRecords() const { return Concepts; } const RecordMap &getObjCCategories() const { return ObjCCategories; } @@ -1065,10 +1184,14 @@ llvm::DenseMap USRBasedLookupTable; RecordMap GlobalFunctions; RecordMap GlobalVariables; + RecordMap Concepts; RecordMap StaticFields; RecordMap Enums; RecordMap Structs; RecordMap CXXClasses; + RecordMap ClassTemplates; + RecordMap ClassTemplateSpecializations; + RecordMap ClassTemplatePartialSpecializations; RecordMap ObjCCategories; RecordMap ObjCInterfaces; RecordMap ObjCProtocols; diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -22,6 +22,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" #include "clang/Lex/MacroInfo.h" #include "llvm/ADT/StringRef.h" #include @@ -161,6 +162,12 @@ return *this; } + DeclarationFragments &pop_back() + { + Fragments.pop_back(); + return *this; + } + /// Append a text Fragment of a space character. /// /// \returns a reference to the DeclarationFragments object itself after @@ -182,6 +189,50 @@ std::vector Fragments; }; +class Template { + struct TemplateParameter { + // "class", "typename", or concept name + std::string Type; + std::string Name; + unsigned int Index; + unsigned int Depth; + bool IsParameterPack; + + TemplateParameter(std::string Type, std::string Name, unsigned int Index, + unsigned int Depth, bool IsParameterPack) + : Type(Type), Name(Name), Index(Index), Depth(Depth), + IsParameterPack(IsParameterPack) {} + }; + + struct TemplateConstraint { + // type name of the constraint, if it has one + std::string Type; + std::string Kind; + std::string LHS, RHS; + }; + SmallVector Parameters; + SmallVector Constraints; + +public: + Template() = default; + + const SmallVector &getParameters() const { + return Parameters; + } + + const SmallVector &getConstraints() const { + return Constraints; + } + + void addTemplateParameter(std::string Type, std::string Name, + unsigned int Index, unsigned int Depth, + bool IsParameterPack) { + Parameters.emplace_back(Type, Name, Index, Depth, IsParameterPack); + } + + bool empty() const { return Parameters.empty() && Constraints.empty(); } +}; + class AccessControl { public: AccessControl(std::string Access) : Access(Access) {} @@ -259,6 +310,50 @@ llvm_unreachable("Unhandled access control"); } + /// Get template details from a template function, class, or variable + static Template getTemplate(const TemplateDecl *Decl) { + Template Template; + for (auto *const Parameter : *Decl->getTemplateParameters()) { + const auto *Param = dyn_cast(Parameter); + if (!Param) // some params are null + continue; + std::string Type; + if (Param->hasTypeConstraint()) + Type = Param->getTypeConstraint()->getNamedConcept()->getName().str(); + else if (Param->wasDeclaredWithTypename()) + Type = "typename"; + else + Type = "class"; + + Template.addTemplateParameter(Type, Param->getName().str(), + Param->getIndex(), Param->getDepth(), + Param->isParameterPack()); + } + return Template; + } + + static Template + getTemplate(const ClassTemplatePartialSpecializationDecl *Decl) { + Template Template; + for (auto *const Parameter : *Decl->getTemplateParameters()) { + const auto *Param = dyn_cast(Parameter); + if (!Param) // some params are null + continue; + std::string Type; + if (Param->hasTypeConstraint()) + Type = Param->getTypeConstraint()->getNamedConcept()->getName().str(); + else if (Param->wasDeclaredWithTypename()) + Type = "typename"; + else + Type = "class"; + + Template.addTemplateParameter(Type, Param->getName().str(), + Param->getIndex(), Param->getDepth(), + Param->isParameterPack()); + } + return Template; + } + /// Build DeclarationFragments for a variable declaration VarDecl. static DeclarationFragments getFragmentsForVar(const VarDecl *); @@ -292,6 +387,24 @@ static DeclarationFragments getFragmentsForOverloadedOperator(const CXXMethodDecl *); + static DeclarationFragments + getFragmentsForTemplateParameters(ArrayRef); + + static DeclarationFragments + getFragmentsForTemplateArguments(const ArrayRef, + const std::optional>); + + static DeclarationFragments getFragmentsForConcept(const ConceptDecl *); + + static DeclarationFragments + getFragmentsForRedeclarableTemplate(const RedeclarableTemplateDecl *); + + static DeclarationFragments getFragmentsForClassTemplateSpecialization( + const ClassTemplateSpecializationDecl *); + + static DeclarationFragments getFragmentsForClassTemplatePartialSpecialization( + const ClassTemplatePartialSpecializationDecl *); + /// Build DeclarationFragments for an Objective-C category declaration /// ObjCCategoryDecl. static DeclarationFragments 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 @@ -15,6 +15,7 @@ #define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/Specifiers.h" #include "clang/ExtractAPI/DeclarationFragments.h" @@ -52,10 +53,24 @@ bool WalkUpFromCXXRecordDecl(const CXXRecordDecl *Decl); + bool WalkUpFromClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *Decl); + + bool WalkUpFromClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *Decl); + bool VisitRecordDecl(const RecordDecl *Decl); bool VisitCXXRecordDecl(const CXXRecordDecl *Decl); + bool VisitConceptDecl(const ConceptDecl *Decl); + + bool VisitClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *Decl); + + bool VisitClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *Decl); + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl); bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl); @@ -328,6 +343,22 @@ return true; } +template +bool ExtractAPIVisitorBase::WalkUpFromClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *Decl) { + getDerivedExtractAPIVisitor().VisitClassTemplateSpecializationDecl(Decl); + return true; +} + +template +bool ExtractAPIVisitorBase:: + WalkUpFromClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *Decl) { + getDerivedExtractAPIVisitor().VisitClassTemplatePartialSpecializationDecl( + Decl); + return true; +} + template bool ExtractAPIVisitorBase::VisitRecordDecl(const RecordDecl *Decl) { if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) @@ -368,7 +399,8 @@ template bool ExtractAPIVisitorBase::VisitCXXRecordDecl( const CXXRecordDecl *Decl) { - if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) + if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl) || + Decl->isImplicit()) return true; StringRef Name = Decl->getName(); @@ -393,9 +425,23 @@ else Kind = APIRecord::RecordKind::RK_CXXClass; - CXXClassRecord *CXXClassRecord = - API.addCXXClass(Name, USR, Loc, AvailabilitySet(Decl), Comment, - Declaration, SubHeading, Kind, isInSystemHeader(Decl)); + CXXClassRecord *CXXClassRecord; + if (Decl->getDescribedClassTemplate()) { + // Inject template fragments before class fragments. + Declaration.insert( + Declaration.begin(), + DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate( + Decl->getDescribedClassTemplate())); + // Cast to easily use previous declaration to get bases. + CXXClassRecord = API.addClassTemplate( + Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, + DeclarationFragmentsBuilder::getTemplate( + Decl->getDescribedClassTemplate()), + isInSystemHeader(Decl)); + } else + CXXClassRecord = + API.addCXXClass(Name, USR, Loc, AvailabilitySet(Decl), Comment, + Declaration, SubHeading, Kind, isInSystemHeader(Decl)); // FIXME: store AccessSpecifier given by inheritance for (const auto BaseSpecifier : Decl->bases()) { @@ -416,6 +462,116 @@ return true; } +template +bool ExtractAPIVisitorBase::VisitConceptDecl(const ConceptDecl *Decl) { + if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) + 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 Declaration = + DeclarationFragmentsBuilder::getFragmentsForConcept(Decl); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(Decl); + API.addConcept(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, + SubHeading, DeclarationFragmentsBuilder::getTemplate(Decl), + isInSystemHeader(Decl)); + return true; +} + +template +bool ExtractAPIVisitorBase::VisitClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *Decl) { + if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) + 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 Declaration = + DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization( + Decl); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(Decl); + + ClassTemplateSpecRecord *ClassTemplateSpecializationRecord = + API.addClassTemplateSpec(Name, USR, Loc, AvailabilitySet(Decl), Comment, + Declaration, SubHeading, isInSystemHeader(Decl)); + + // FIXME: store AccessSpecifier given by inheritance + for (const auto BaseSpecifier : Decl->bases()) { + SymbolReference BaseClass; + CXXRecordDecl *BaseClassDecl = + BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl(); + BaseClass.Name = BaseClassDecl->getName(); + BaseClass.USR = API.recordUSR(BaseClassDecl); + ClassTemplateSpecializationRecord->Bases.emplace_back(BaseClass); + } + + getDerivedExtractAPIVisitor().recordCXXFields( + ClassTemplateSpecializationRecord, Decl->fields()); + getDerivedExtractAPIVisitor().recordCXXMethods( + ClassTemplateSpecializationRecord, Decl->methods()); + return true; +} + +template +bool ExtractAPIVisitorBase:: + VisitClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *Decl) { + if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) + 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 Declaration = DeclarationFragmentsBuilder:: + getFragmentsForClassTemplatePartialSpecialization(Decl); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(Decl); + + ClassTemplatePartialSpecRecord *ClassTemplatePartialSpecRecord = + API.addClassTemplatePartialSpec( + Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, + SubHeading, DeclarationFragmentsBuilder::getTemplate(Decl), + isInSystemHeader(Decl)); + + // FIXME: store AccessSpecifier given by inheritance + for (const auto BaseSpecifier : Decl->bases()) { + SymbolReference BaseClass; + CXXRecordDecl *BaseClassDecl = + BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl(); + BaseClass.Name = BaseClassDecl->getName(); + BaseClass.USR = API.recordUSR(BaseClassDecl); + ClassTemplatePartialSpecRecord->Bases.emplace_back(BaseClass); + } + + getDerivedExtractAPIVisitor().recordCXXFields(ClassTemplatePartialSpecRecord, + Decl->fields()); + getDerivedExtractAPIVisitor().recordCXXMethods(ClassTemplatePartialSpecRecord, + Decl->methods()); + return true; +} + template bool ExtractAPIVisitorBase::VisitObjCInterfaceDecl( const ObjCInterfaceDecl *Decl) { 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 @@ -33,6 +33,14 @@ getDerived()->traverseCXXClassRecords(); + getDerived()->traverseClassTemplateRecords(); + + getDerived()->traverseClassTemplateSpecRecords(); + + getDerived()->traverseClassTemplatePartialSpecRecords(); + + getDerived()->traverseConcepts(); + getDerived()->traverseStructRecords(); getDerived()->traverseObjCInterfaces(); @@ -74,6 +82,28 @@ getDerived()->visitCXXClassRecord(*Class.second); } + void traverseClassTemplateRecords() { + for (const auto &ClassTemplate : API.getClassTemplates()) + getDerived()->visitClassTemplateRecord(*ClassTemplate.second); + } + + void traverseClassTemplateSpecRecords() { + for (const auto &ClassTemplateSpec : API.getClassTemplateSpecializations()) + getDerived()->visitClassTemplateSpecRecord(*ClassTemplateSpec.second); + } + + void traverseClassTemplatePartialSpecRecords() { + for (const auto &ClassTemplatePartialSpec : + API.getClassTemplatePartialSpecializations()) + getDerived()->visitClassTemplatePartialSpecRecord( + *ClassTemplatePartialSpec.second); + } + + void traverseConcepts() { + for (const auto &Concept : API.getConcepts()) + getDerived()->visitConceptRecord(*Concept.second); + } + void traverseObjCInterfaces() { for (const auto &Interface : API.getObjCInterfaces()) getDerived()->visitObjCContainerRecord(*Interface.second); @@ -110,6 +140,8 @@ void visitCXXClassRecord(const CXXClassRecord &Record){}; + void visitClassTemplateRecord(const ClassTemplateRecord &Record){}; + /// Visit an Objective-C container record. void visitObjCContainerRecord(const ObjCContainerRecord &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 @@ -92,6 +92,10 @@ /// Get the string representation of the relationship kind. static StringRef getRelationshipString(RelationshipKind Kind); + enum ConstraintKind { Conformance, ConditionalConformance }; + + static StringRef getConstraintString(ConstraintKind Kind); + private: /// Just serialize the currently recorded objects in Symbol Graph format. Object serializeCurrentGraph(); @@ -164,6 +168,15 @@ void visitCXXClassRecord(const CXXClassRecord &Record); + void visitClassTemplateRecord(const ClassTemplateRecord &Record); + + void visitClassTemplateSpecRecord(const ClassTemplateSpecRecord &Record); + + void visitClassTemplatePartialSpecRecord( + const ClassTemplatePartialSpecRecord &Record); + + void visitConceptRecord(const ConceptRecord &Record); + /// Visit an Objective-C container record. void visitObjCContainerRecord(const ObjCContainerRecord &Record); 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 @@ -159,6 +159,49 @@ SubHeading, Kind, IsFromSystemHeader); } +ClassTemplateRecord *APISet::addClassTemplate( + StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + Template Template, bool IsFromSystemHeader) { + + return addTopLevelRecord(USRBasedLookupTable, ClassTemplates, USR, Name, Loc, + std::move(Availability), Comment, Declaration, + SubHeading, Template, IsFromSystemHeader); +} + +ClassTemplateSpecRecord *APISet::addClassTemplateSpec( + StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, ClassTemplateSpecializations, + USR, Name, Loc, std::move(Availability), Comment, + Declaration, SubHeading, IsFromSystemHeader); +} + +ClassTemplatePartialSpecRecord *APISet::addClassTemplatePartialSpec( + StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + Template Template, bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, + ClassTemplatePartialSpecializations, USR, Name, Loc, + std::move(Availability), Comment, Declaration, + SubHeading, Template, IsFromSystemHeader); +} + +ConceptRecord *APISet::addConcept(StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + Template Template, bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, Concepts, USR, Name, Loc, + std::move(Availability), Comment, Declaration, + SubHeading, Template, IsFromSystemHeader); +} + CXXMethodRecord *APISet::addCXXMethod( CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, 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 @@ -735,6 +735,152 @@ return Fragments.append(";", DeclarationFragments::FragmentKind::Text); } +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForTemplateParameters( + ArrayRef ParameterArray) { + DeclarationFragments Fragments; + for (unsigned i = 0, end = ParameterArray.size(); i != end; ++i) { + if (i) + Fragments.append(",", DeclarationFragments::FragmentKind::Text) + .appendSpace(); + + const auto *TemplateParam = + dyn_cast(ParameterArray[i]); + if (!TemplateParam) + continue; + if (TemplateParam->hasTypeConstraint()) + Fragments.append(TemplateParam->getTypeConstraint() + ->getNamedConcept() + ->getName() + .str(), + DeclarationFragments::FragmentKind::TypeIdentifier); + else if (TemplateParam->wasDeclaredWithTypename()) + Fragments.append("typename", DeclarationFragments::FragmentKind::Keyword); + else + Fragments.append("class", DeclarationFragments::FragmentKind::Keyword); + + if (TemplateParam->isParameterPack()) + Fragments.append("...", DeclarationFragments::FragmentKind::Text); + + Fragments.appendSpace().append( + TemplateParam->getName(), + DeclarationFragments::FragmentKind::GenericParameter); + } + return Fragments; +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForTemplateArguments( + const ArrayRef TemplateArguments, + const std::optional> TemplateParameters) { + DeclarationFragments Fragments; + for (unsigned i = 0, end = TemplateArguments.size(); i != end; ++i) { + if (i) + Fragments.append(",", DeclarationFragments::FragmentKind::Text) + .appendSpace(); + + std::string Type = TemplateArguments[i].getAsType().getAsString(); + std::string Name; + std::string ex = Type.substr(0, 14); + if (Type.substr(0, 14).compare("type-parameter") == 0) { + // The arg is a template parameter from a partial spec. + if (TemplateParameters) + for (unsigned j = 0; j < TemplateParameters->size(); ++j) { + const auto *Parameter = + dyn_cast((*TemplateParameters)[j]); + if (Type.compare("type-parameter-" + + std::to_string(Parameter->getIndex()) + + std::to_string(Parameter->getDepth()))) + Name = (*TemplateParameters)[j]->getName(); + } + } else + Name = Type; + + Fragments.append(Name, DeclarationFragments::FragmentKind::TypeIdentifier); + if (TemplateArguments[i].isPackExpansion()) + Fragments.append("...", DeclarationFragments::FragmentKind::Text); + } + return Fragments; +} + +DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForConcept( + const ConceptDecl *Concept) { + DeclarationFragments Fragments; + return Fragments + .append("template", DeclarationFragments::FragmentKind::Keyword) + .append("<", DeclarationFragments::FragmentKind::Text) + .append(getFragmentsForTemplateParameters( + Concept->getTemplateParameters()->asArray())) + .append("> ", DeclarationFragments::FragmentKind::Text) + .append("concept", DeclarationFragments::FragmentKind::Keyword) + .appendSpace() + .append(Concept->getName().str(), + DeclarationFragments::FragmentKind::Identifier) + .append(";", DeclarationFragments::FragmentKind::Text); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate( + const RedeclarableTemplateDecl *RedeclarableTemplate) { + DeclarationFragments Fragments; + Fragments.append("template", DeclarationFragments::FragmentKind::Keyword) + .append("<", DeclarationFragments::FragmentKind::Text) + .append(getFragmentsForTemplateParameters( + RedeclarableTemplate->getTemplateParameters()->asArray())) + .append(">", DeclarationFragments::FragmentKind::Text) + .appendSpace(); + + if (isa(RedeclarableTemplate)) + Fragments.appendSpace() + .append("using", DeclarationFragments::FragmentKind::Keyword) + .appendSpace() + .append(RedeclarableTemplate->getName(), + DeclarationFragments::FragmentKind::Identifier); + // the templated records will be resposbible for injecting their templates + return Fragments.appendSpace(); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForClassTemplateSpecialization( + const ClassTemplateSpecializationDecl *Decl) { + DeclarationFragments Fragments; + return Fragments + .append("template", DeclarationFragments::FragmentKind::Keyword) + .append("<", DeclarationFragments::FragmentKind::Text) + .append(">", DeclarationFragments::FragmentKind::Text) + .appendSpace() + .append(DeclarationFragmentsBuilder::getFragmentsForCXXClass( + cast(Decl))) + .pop_back() // there is an extra semicolon now + .append("<", DeclarationFragments::FragmentKind::Text) + .append(getFragmentsForTemplateArguments( + Decl->getTemplateArgs().asArray(), std::nullopt)) + .append(">", DeclarationFragments::FragmentKind::Text) + .append(";", DeclarationFragments::FragmentKind::Text); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForClassTemplatePartialSpecialization( + const ClassTemplatePartialSpecializationDecl *Decl) { + DeclarationFragments Fragments; + return Fragments + .append("template", DeclarationFragments::FragmentKind::Keyword) + .append("<", DeclarationFragments::FragmentKind::Text) + .append(getFragmentsForTemplateParameters( + Decl->getTemplateParameters()->asArray())) + .append(">", DeclarationFragments::FragmentKind::Text) + .appendSpace() + .append(DeclarationFragmentsBuilder::getFragmentsForCXXClass( + cast(Decl))) + .pop_back() // there is an extra semicolon now + .append("<", DeclarationFragments::FragmentKind::Text) + .append(getFragmentsForTemplateArguments( + Decl->getTemplateArgs().asArray(), + Decl->getTemplateParameters()->asArray())) + .append(">", DeclarationFragments::FragmentKind::Text) + .append(";", DeclarationFragments::FragmentKind::Text); +} + DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name, const MacroDirective *MD) { 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 @@ -387,10 +387,17 @@ Kind["identifier"] = AddLangPrefix("type.property"); Kind["displayName"] = "Type Property"; break; + case APIRecord::RK_ClassTemplate: + case APIRecord::RK_ClassTemplateSpecialization: + case APIRecord::RK_ClassTemplatePartialSpecialization: case APIRecord::RK_CXXClass: Kind["identifier"] = AddLangPrefix("class"); Kind["displayName"] = "Class"; break; + case APIRecord::RK_Concept: + Kind["identifier"] = AddLangPrefix("concept"); + Kind["displayName"] = "Concept"; + break; case APIRecord::RK_CXXStaticMethod: Kind["identifier"] = AddLangPrefix("type.method"); Kind["displayName"] = "Static Method"; @@ -536,6 +543,52 @@ serializeString(Paren, "accessLevel", accessLevel); } +template +std::optional serializeTemplateMixinImpl(const RecordTy &Record, + std::true_type) { + const auto &Template = Record.Templ; + if (Template.empty()) + return std::nullopt; + + Object Generics; + Array GenericParameters; + for (const auto Param : Template.getParameters()) { + Object Parameter; + Parameter["name"] = Param.Name; + Parameter["index"] = Param.Index; + Parameter["depth"] = Param.Depth; + GenericParameters.emplace_back(std::move(Parameter)); + } + if (!GenericParameters.empty()) + Generics["parameters"] = std::move(GenericParameters); + + Array GenericConstraints; + for (const auto Constr : Template.getConstraints()) { + Object Constraint; + Constraint["kind"] = Constr.Kind; + Constraint["lhs"] = Constr.LHS; + Constraint["rhs"] = Constr.RHS; + GenericConstraints.emplace_back(std::move(Constraint)); + } + + if (!GenericConstraints.empty()) + Generics["constraints"] = std::move(GenericConstraints); + + return Generics; +} + +template +std::optional serializeTemplateMixinImpl(const RecordTy &Record, + std::false_type) { + return std::nullopt; +} + +template +void serializeTemplateMixin(Object &Paren, const RecordTy &Record) { + serializeObject(Paren, "swiftGenerics", + serializeTemplateMixinImpl(Record, has_template())); +} + struct PathComponent { StringRef USR; StringRef Name; @@ -680,6 +733,7 @@ serializeFunctionSignatureMixin(Obj, Record); serializeAccessMixin(Obj, Record); + serializeTemplateMixin(Obj, Record); return Obj; } @@ -713,6 +767,16 @@ llvm_unreachable("Unhandled relationship kind"); } +StringRef SymbolGraphSerializer::getConstraintString(ConstraintKind Kind) { + switch (Kind) { + case ConstraintKind::Conformance: + return "conformance"; + case ConstraintKind::ConditionalConformance: + return "conditionalConformance"; + } + llvm_unreachable("Unhandled constraint kind"); +} + void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, SymbolReference Source, SymbolReference Target) { @@ -783,6 +847,56 @@ serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); } +void SymbolGraphSerializer::visitClassTemplateRecord( + const ClassTemplateRecord &Record) { + auto Class = serializeAPIRecord(Record); + if (!Class) + return; + + Symbols.emplace_back(std::move(*Class)); + serializeMembers(Record, Record.Fields); + serializeMembers(Record, Record.Methods); + + for (const auto Base : Record.Bases) + serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); +} + +void SymbolGraphSerializer::visitClassTemplateSpecRecord( + const ClassTemplateSpecRecord &Record) { + auto Class = serializeAPIRecord(Record); + if (!Class) + return; + + Symbols.emplace_back(std::move(*Class)); + serializeMembers(Record, Record.Fields); + serializeMembers(Record, Record.Methods); + + for (const auto Base : Record.Bases) + serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); +} + +void SymbolGraphSerializer::visitClassTemplatePartialSpecRecord( + const ClassTemplatePartialSpecRecord &Record) { + auto Class = serializeAPIRecord(Record); + if (!Class) + return; + + Symbols.emplace_back(std::move(*Class)); + serializeMembers(Record, Record.Fields); + serializeMembers(Record, Record.Methods); + + for (const auto Base : Record.Bases) + serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); +} + +void SymbolGraphSerializer::visitConceptRecord(const ConceptRecord &Record) { + auto Concept = serializeAPIRecord(Record); + if (!Concept) + return; + + Symbols.emplace_back(std::move(*Concept)); +} + void SymbolGraphSerializer::visitObjCContainerRecord( const ObjCContainerRecord &Record) { auto ObjCContainer = serializeAPIRecord(Record); diff --git a/clang/test/ExtractAPI/class_template.cpp b/clang/test/ExtractAPI/class_template.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/class_template.cpp @@ -0,0 +1,133 @@ +// 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 class Foo {}; + +/// 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": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@ST>1#T@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 28, + "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" + } + ] + } + } + ] +} diff --git a/clang/test/ExtractAPI/class_template_partial_spec.cpp b/clang/test/ExtractAPI/class_template_partial_spec.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/class_template_partial_spec.cpp @@ -0,0 +1,259 @@ +// 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 class Foo {}; + +template class Foo {}; + +/// 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": "X" + }, + { + "kind": "text", + "spelling": ", " + }, + { + "kind": "keyword", + "spelling": "typename" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "genericParameter", + "spelling": "Y" + }, + { + "kind": "text", + "spelling": "> " + }, + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@ST>2#T#T@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 40, + "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": "X" + }, + { + "depth": 0, + "index": 1, + "name": "Y" + } + ] + } + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "template" + }, + { + "kind": "text", + "spelling": "<" + }, + { + "kind": "keyword", + "spelling": "typename" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "genericParameter", + "spelling": "Z" + }, + { + "kind": "text", + "spelling": "> " + }, + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": "<" + }, + { + "kind": "typeIdentifier", + "spelling": "Z" + }, + { + "kind": "text", + "spelling": ", " + }, + { + "kind": "typeIdentifier", + "spelling": "int" + }, + { + "kind": "text", + "spelling": ">;" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@SP>1#T@Foo>#t0.0#I" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 28, + "line": 3 + }, + "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": "Z" + } + ] + } + } + ] +} diff --git a/clang/test/ExtractAPI/class_template_spec.cpp b/clang/test/ExtractAPI/class_template_spec.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/class_template_spec.cpp @@ -0,0 +1,205 @@ +// 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 class Foo {}; + +template<> class Foo {}; + +/// 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": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@ST>1#T@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 28, + "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": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": "<" + }, + { + "kind": "typeIdentifier", + "spelling": "int" + }, + { + "kind": "text", + "spelling": ">;" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo>#I" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 18, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/concept.cpp b/clang/test/ExtractAPI/concept.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/concept.cpp @@ -0,0 +1,133 @@ +// 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 -std=c++20 -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 concept Foo = true; + +/// 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": "keyword", + "spelling": "concept" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@CT@Foo" + }, + "kind": { + "displayName": "Concept", + "identifier": "c++.concept" + }, + "location": { + "position": { + "character": 30, + "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" + } + ] + } + } + ] +}