Author: Erick Velez Date: 2023-08-18T13:40:22-07:00 New Revision: 7ba37f4e46a5bbb1dc42f1ea1722296ea32034d5
URL: https://github.com/llvm/llvm-project/commit/7ba37f4e46a5bbb1dc42f1ea1722296ea32034d5 DIFF: https://github.com/llvm/llvm-project/commit/7ba37f4e46a5bbb1dc42f1ea1722296ea32034d5.diff LOG: [clang][ExtractAPI] Add support for C++ class templates and concepts Add has_template template, DeclarationFragmentBuilder functions, and tests for class templates, specializations/partial specs, and concepts. Depends on D157007 Reviewed By: dang Differential Revision: https://reviews.llvm.org/D157076 Added: clang/test/ExtractAPI/class_template.cpp clang/test/ExtractAPI/class_template_param_inheritance.cpp clang/test/ExtractAPI/class_template_partial_spec.cpp clang/test/ExtractAPI/class_template_spec.cpp clang/test/ExtractAPI/concept.cpp Modified: clang/include/clang/ExtractAPI/API.h clang/include/clang/ExtractAPI/DeclarationFragments.h clang/include/clang/ExtractAPI/ExtractAPIVisitor.h clang/include/clang/ExtractAPI/Serialization/SerializerBase.h clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h clang/lib/ExtractAPI/API.cpp clang/lib/ExtractAPI/DeclarationFragments.cpp clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp Removed: ################################################################################ diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h index a965f49c8e91b2..83ff982be559b9 100644 --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -36,6 +36,86 @@ namespace clang { namespace extractapi { +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; + }; + llvm::SmallVector<TemplateParameter> Parameters; + llvm::SmallVector<TemplateConstraint> Constraints; + +public: + Template() = default; + + Template(const TemplateDecl *Decl) { + for (auto *const Parameter : *Decl->getTemplateParameters()) { + const auto *Param = dyn_cast<TemplateTypeParmDecl>(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"; + + addTemplateParameter(Type, Param->getName().str(), Param->getIndex(), + Param->getDepth(), Param->isParameterPack()); + } + } + + Template(const ClassTemplatePartialSpecializationDecl *Decl) { + for (auto *const Parameter : *Decl->getTemplateParameters()) { + const auto *Param = dyn_cast<TemplateTypeParmDecl>(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"; + + addTemplateParameter(Type, Param->getName().str(), Param->getIndex(), + Param->getDepth(), Param->isParameterPack()); + } + } + + const llvm::SmallVector<TemplateParameter> &getParameters() const { + return Parameters; + } + + const llvm::SmallVector<TemplateConstraint> &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(); } +}; + /// DocComment is a vector of RawComment::CommentLine. /// /// Each line represents one line of striped documentation comment, @@ -69,6 +149,10 @@ struct APIRecord { RK_StaticField, RK_CXXField, RK_CXXClass, + RK_ClassTemplate, + RK_ClassTemplateSpecialization, + RK_ClassTemplatePartialSpecialization, + RK_Concept, RK_CXXStaticMethod, RK_CXXInstanceMethod, RK_CXXConstructorMethod, @@ -644,6 +728,75 @@ struct CXXClassRecord : APIRecord { 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 ClassTemplateSpecializationRecord : CXXClassRecord { + ClassTemplateSpecializationRecord(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 ClassTemplatePartialSpecializationRecord : CXXClassRecord { + Template Templ; + ClassTemplatePartialSpecializationRecord( + 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; @@ -779,6 +932,13 @@ template <typename RecordTy> struct has_access : public std::false_type {}; template <> struct has_access<CXXMethodRecord> : public std::true_type {}; template <> struct has_access<CXXFieldRecord> : public std::true_type {}; +template <typename RecordTy> struct has_template : public std::false_type {}; +template <> struct has_template<ClassTemplateRecord> : public std::true_type {}; +template <> +struct has_template<ClassTemplatePartialSpecializationRecord> + : public std::true_type {}; +template <> struct has_template<ConceptRecord> : public std::true_type {}; + /// APISet holds the set of API records collected from given inputs. class APISet { public: @@ -876,6 +1036,26 @@ class APISet { 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); + + ClassTemplateSpecializationRecord *addClassTemplateSpecialization( + StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + bool IsFromSystemHeader); + + ClassTemplatePartialSpecializationRecord * + addClassTemplatePartialSpecialization( + 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, @@ -890,6 +1070,13 @@ class APISet { 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 @@ -1018,6 +1205,19 @@ class APISet { const RecordMap<EnumRecord> &getEnums() const { return Enums; } const RecordMap<StructRecord> &getStructs() const { return Structs; } const RecordMap<CXXClassRecord> &getCXXClasses() const { return CXXClasses; } + const RecordMap<ConceptRecord> &getConcepts() const { return Concepts; } + const RecordMap<ClassTemplateRecord> &getClassTemplates() const { + return ClassTemplates; + } + const RecordMap<ClassTemplateSpecializationRecord> & + getClassTemplateSpecializations() const { + return ClassTemplateSpecializations; + } + const RecordMap<ClassTemplatePartialSpecializationRecord> & + getClassTemplatePartialSpecializations() const { + return ClassTemplatePartialSpecializations; + } + const RecordMap<ConceptRecord> &getRecords() const { return Concepts; } const RecordMap<ObjCCategoryRecord> &getObjCCategories() const { return ObjCCategories; } @@ -1071,10 +1271,15 @@ class APISet { llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable; RecordMap<GlobalFunctionRecord> GlobalFunctions; RecordMap<GlobalVariableRecord> GlobalVariables; + RecordMap<ConceptRecord> Concepts; RecordMap<StaticFieldRecord> StaticFields; RecordMap<EnumRecord> Enums; RecordMap<StructRecord> Structs; RecordMap<CXXClassRecord> CXXClasses; + RecordMap<ClassTemplateRecord> ClassTemplates; + RecordMap<ClassTemplateSpecializationRecord> ClassTemplateSpecializations; + RecordMap<ClassTemplatePartialSpecializationRecord> + ClassTemplatePartialSpecializations; RecordMap<ObjCCategoryRecord> ObjCCategories; RecordMap<ObjCInterfaceRecord> ObjCInterfaces; RecordMap<ObjCProtocolRecord> ObjCProtocols; diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h index dc8a02e8811aed..c0271de91acfb2 100644 --- 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 <vector> @@ -161,6 +162,11 @@ class DeclarationFragments { 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 @@ -292,6 +298,28 @@ class DeclarationFragmentsBuilder { static DeclarationFragments getFragmentsForOverloadedOperator(const CXXMethodDecl *); + static DeclarationFragments + getFragmentsForTemplateParameters(ArrayRef<NamedDecl *>); + + static std::string getNameForTemplateArgument(const ArrayRef<NamedDecl *>, + std::string); + + static DeclarationFragments + getFragmentsForTemplateArguments(const ArrayRef<TemplateArgument>, + ASTContext &, + const std::optional<ArrayRef<NamedDecl *>>); + + 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 index 904942a69691a6..0392b6db2da0ad 100644 --- 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 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> { 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); @@ -128,6 +143,29 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> { Derived &getDerivedExtractAPIVisitor() { return *static_cast<Derived *>(this); } + + SmallVector<SymbolReference> getBases(const CXXRecordDecl *Decl) { + // FIXME: store AccessSpecifier given by inheritance + SmallVector<SymbolReference> Bases; + for (const auto BaseSpecifier : Decl->bases()) { + // skip classes not inherited as public + if (BaseSpecifier.getAccessSpecifier() != AccessSpecifier::AS_public) + continue; + SymbolReference BaseClass; + if (BaseSpecifier.getType().getTypePtr()->isTemplateTypeParmType()) { + BaseClass.Name = API.copyString(BaseSpecifier.getType().getAsString()); + BaseClass.USR = API.recordUSR( + BaseSpecifier.getType()->getAs<TemplateTypeParmType>()->getDecl()); + } else { + CXXRecordDecl *BaseClassDecl = + BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl(); + BaseClass.Name = BaseClassDecl->getName(); + BaseClass.USR = API.recordUSR(BaseClassDecl); + } + Bases.emplace_back(BaseClass); + } + return Bases; + } }; template <typename T> @@ -328,6 +366,22 @@ bool ExtractAPIVisitorBase<Derived>::WalkUpFromCXXRecordDecl( return true; } +template <typename Derived> +bool ExtractAPIVisitorBase<Derived>::WalkUpFromClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *Decl) { + getDerivedExtractAPIVisitor().VisitClassTemplateSpecializationDecl(Decl); + return true; +} + +template <typename Derived> +bool ExtractAPIVisitorBase<Derived>:: + WalkUpFromClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *Decl) { + getDerivedExtractAPIVisitor().VisitClassTemplatePartialSpecializationDecl( + Decl); + return true; +} + template <typename Derived> bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) { if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) @@ -368,7 +422,8 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) { template <typename Derived> bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl( const CXXRecordDecl *Decl) { - if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) + if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl) || + Decl->isImplicit()) return true; StringRef Name = Decl->getName(); @@ -393,22 +448,22 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl( 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())); + CXXClassRecord = API.addClassTemplate( + Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, + Template(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()) { - // skip classes not inherited as public - if (BaseSpecifier.getAccessSpecifier() != AccessSpecifier::AS_public) - continue; - SymbolReference BaseClass; - CXXRecordDecl *BaseClassDecl = - BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl(); - BaseClass.Name = BaseClassDecl->getName(); - BaseClass.USR = API.recordUSR(BaseClassDecl); - CXXClassRecord->Bases.emplace_back(BaseClass); - } + CXXClassRecord->Bases = getBases(Decl); getDerivedExtractAPIVisitor().recordCXXFields(CXXClassRecord, Decl->fields()); getDerivedExtractAPIVisitor().recordCXXMethods(CXXClassRecord, @@ -416,6 +471,97 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl( return true; } +template <typename Derived> +bool ExtractAPIVisitorBase<Derived>::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, Template(Decl), isInSystemHeader(Decl)); + return true; +} + +template <typename Derived> +bool ExtractAPIVisitorBase<Derived>::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); + + auto *ClassTemplateSpecializationRecord = API.addClassTemplateSpecialization( + Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, + isInSystemHeader(Decl)); + + ClassTemplateSpecializationRecord->Bases = getBases(Decl); + getDerivedExtractAPIVisitor().recordCXXFields( + ClassTemplateSpecializationRecord, Decl->fields()); + getDerivedExtractAPIVisitor().recordCXXMethods( + ClassTemplateSpecializationRecord, Decl->methods()); + return true; +} + +template <typename Derived> +bool ExtractAPIVisitorBase<Derived>:: + 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); + + auto *ClassTemplatePartialSpecRecord = + API.addClassTemplatePartialSpecialization( + Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, + SubHeading, Template(Decl), isInSystemHeader(Decl)); + + ClassTemplatePartialSpecRecord->Bases = getBases(Decl); + + getDerivedExtractAPIVisitor().recordCXXFields(ClassTemplatePartialSpecRecord, + Decl->fields()); + getDerivedExtractAPIVisitor().recordCXXMethods(ClassTemplatePartialSpecRecord, + Decl->methods()); + return true; +} + template <typename Derived> bool ExtractAPIVisitorBase<Derived>::VisitObjCInterfaceDecl( const ObjCInterfaceDecl *Decl) { diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h index 68c20b3f6c6f9d..2bcd480b60e1c9 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -33,6 +33,14 @@ template <typename Derived> class APISetVisitor { getDerived()->traverseCXXClassRecords(); + getDerived()->traverseClassTemplateRecords(); + + getDerived()->traverseClassTemplateSpecializationRecords(); + + getDerived()->traverseClassTemplatePartialSpecializationRecords(); + + getDerived()->traverseConcepts(); + getDerived()->traverseStructRecords(); getDerived()->traverseObjCInterfaces(); @@ -76,6 +84,30 @@ template <typename Derived> class APISetVisitor { getDerived()->visitCXXClassRecord(*Class.second); } + void traverseClassTemplateRecords() { + for (const auto &ClassTemplate : API.getClassTemplates()) + getDerived()->visitClassTemplateRecord(*ClassTemplate.second); + } + + void traverseClassTemplateSpecializationRecords() { + for (const auto &ClassTemplateSpecialization : + API.getClassTemplateSpecializations()) + getDerived()->visitClassTemplateSpecializationRecord( + *ClassTemplateSpecialization.second); + } + + void traverseClassTemplatePartialSpecializationRecords() { + for (const auto &ClassTemplatePartialSpecialization : + API.getClassTemplatePartialSpecializations()) + getDerived()->visitClassTemplatePartialSpecializationRecord( + *ClassTemplatePartialSpecialization.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); @@ -117,6 +149,14 @@ template <typename Derived> class APISetVisitor { void visitCXXClassRecord(const CXXClassRecord &Record){}; + void visitClassTemplateRecord(const ClassTemplateRecord &Record){}; + + void visitClassTemplateSpecializationRecord( + const ClassTemplateSpecializationRecord &Record){}; + + void visitClassTemplatePartialSpecializationRecord( + const ClassTemplatePartialSpecializationRecord &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 index ab60b5c71f2289..7b315c9f6d410e 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -97,6 +97,10 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> { /// 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(); @@ -171,6 +175,16 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> { void visitCXXClassRecord(const CXXClassRecord &Record); + void visitClassTemplateRecord(const ClassTemplateRecord &Record); + + void visitClassTemplateSpecializationRecord( + const ClassTemplateSpecializationRecord &Record); + + void visitClassTemplatePartialSpecializationRecord( + const ClassTemplatePartialSpecializationRecord &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 index d9baa188f1be5b..c1c4bc7a0b4cf7 100644 --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -159,6 +159,50 @@ APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc, 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); +} + +ClassTemplateSpecializationRecord *APISet::addClassTemplateSpecialization( + 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); +} + +ClassTemplatePartialSpecializationRecord * +APISet::addClassTemplatePartialSpecialization( + 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 index 703142a910764c..ec8ae56f71e161 100644 --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -735,6 +735,174 @@ DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator( return Fragments.append(";", DeclarationFragments::FragmentKind::Text); } +// Get fragments for template parameters, e.g. T in tempalte<typename T> ... +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForTemplateParameters( + ArrayRef<NamedDecl *> 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<TemplateTypeParmDecl>(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; +} + +// Find the name of a template argument from the template's parameters. +std::string DeclarationFragmentsBuilder::getNameForTemplateArgument( + const ArrayRef<NamedDecl *> TemplateParameters, std::string TypeParameter) { + // The arg is a generic parameter from a partial spec, e.g. + // T in template<typename T> Foo<T, int>. + // + // Those names appear as "type-parameter-<index>-<depth>", so we must find its + // name from the template's parameter list. + for (unsigned i = 0; i < TemplateParameters.size(); ++i) { + const auto *Parameter = + dyn_cast<TemplateTypeParmDecl>(TemplateParameters[i]); + if (TypeParameter.compare("type-parameter-" + + std::to_string(Parameter->getDepth()) + "-" + + std::to_string(Parameter->getIndex())) == 0) + return std::string(TemplateParameters[i]->getName()); + } + llvm_unreachable("Could not find the name of a template argument."); +} + +// Get fragments for template arguments, e.g. int in template<typename T> +// Foo<int>; +// +// Note: TemplateParameters is only necessary if the Decl is a +// PartialSpecialization, where we need the parameters to deduce the name of the +// generic arguments. +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForTemplateArguments( + const ArrayRef<TemplateArgument> TemplateArguments, ASTContext &Context, + const std::optional<ArrayRef<NamedDecl *>> 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(); + DeclarationFragments After; + DeclarationFragments ArgumentFragment = + getFragmentsForType(TemplateArguments[i].getAsType(), Context, After); + + if (ArgumentFragment.begin()->Spelling.substr(0, 14).compare( + "type-parameter") == 0) { + std::string ProperArgName = getNameForTemplateArgument( + TemplateParameters.value(), ArgumentFragment.begin()->Spelling); + ArgumentFragment.begin()->Spelling.swap(ProperArgName); + } + Fragments.append(std::move(ArgumentFragment)); + + 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<TypeAliasTemplateDecl>(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<CXXRecordDecl>(Decl))) + .pop_back() // there is an extra semicolon now + .append("<", DeclarationFragments::FragmentKind::Text) + .append( + getFragmentsForTemplateArguments(Decl->getTemplateArgs().asArray(), + Decl->getASTContext(), 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<CXXRecordDecl>(Decl))) + .pop_back() // there is an extra semicolon now + .append("<", DeclarationFragments::FragmentKind::Text) + .append(getFragmentsForTemplateArguments( + Decl->getTemplateArgs().asArray(), Decl->getASTContext(), + 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 index af52abe9f2e144..6c7a037f76e348 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -393,10 +393,17 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) { 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"; @@ -545,6 +552,52 @@ void serializeAccessMixin(Object &Paren, const RecordTy &Record) { serializeString(Paren, "accessLevel", accessLevel); } +template <typename RecordTy> +std::optional<Object> 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 <typename RecordTy> +std::optional<Object> serializeTemplateMixinImpl(const RecordTy &Record, + std::false_type) { + return std::nullopt; +} + +template <typename RecordTy> +void serializeTemplateMixin(Object &Paren, const RecordTy &Record) { + serializeObject(Paren, "swiftGenerics", + serializeTemplateMixinImpl(Record, has_template<RecordTy>())); +} + struct PathComponent { StringRef USR; StringRef Name; @@ -691,6 +744,7 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { serializeFunctionSignatureMixin(Obj, Record); serializeAccessMixin(Obj, Record); + serializeTemplateMixin(Obj, Record); return Obj; } @@ -726,6 +780,16 @@ StringRef SymbolGraphSerializer::getRelationshipString(RelationshipKind Kind) { 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) { @@ -796,6 +860,56 @@ void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) { 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::visitClassTemplateSpecializationRecord( + const ClassTemplateSpecializationRecord &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::visitClassTemplatePartialSpecializationRecord( + const ClassTemplatePartialSpecializationRecord &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 index 00000000000000..25895a6fefe06d --- /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<typename T> 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_param_inheritance.cpp b/clang/test/ExtractAPI/class_template_param_inheritance.cpp new file mode 100644 index 00000000000000..0e50e6081c914d --- /dev/null +++ b/clang/test/ExtractAPI/class_template_param_inheritance.cpp @@ -0,0 +1,140 @@ +// 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> class Foo : public T {}; + +/// 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": "inheritsFrom", + "source": "c:@ST>1#T@Foo", + "target": "", + "targetFallback": "T" + } + ], + "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 index 00000000000000..294bbb726bcf08 --- /dev/null +++ b/clang/test/ExtractAPI/class_template_partial_spec.cpp @@ -0,0 +1,261 @@ +// 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 X, typename Y> class Foo {}; + +template<typename Z> class Foo<Z, int> {}; + +/// 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", + "preciseIdentifier": "c:t0.0", + "spelling": "Z" + }, + { + "kind": "text", + "spelling": ", " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "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 index 00000000000000..166b03911db7dc --- /dev/null +++ b/clang/test/ExtractAPI/class_template_spec.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 +template<typename T> class Foo {}; + +template<> class Foo<int> {}; + +/// 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", + "preciseIdentifier": "c:I", + "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 index 00000000000000..08c3c832ce3a06 --- /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<typename T> 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" + } + ] + } + } + ] +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits