Mercurial > hg > CbC > CbC_llvm
diff clang-tools-extra/clang-doc/Serialize.cpp @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children | 0572611fdcc8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/clang-tools-extra/clang-doc/Serialize.cpp Thu Feb 13 15:10:13 2020 +0900 @@ -0,0 +1,666 @@ +//===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Serialize.h" +#include "BitcodeWriter.h" +#include "clang/AST/Comment.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/SHA1.h" + +using clang::comments::FullComment; + +namespace clang { +namespace doc { +namespace serialize { + +SymbolID hashUSR(llvm::StringRef USR) { + return llvm::SHA1::hash(arrayRefFromStringRef(USR)); +} + +template <typename T> +static void +populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, + const T *D, bool &IsAnonymousNamespace); + +// A function to extract the appropriate relative path for a given info's +// documentation. The path returned is a composite of the parent namespaces. +// +// Example: Given the below, the directory path for class C info will be +// <root>/A/B +// +// namespace A { +// namesapce B { +// +// class C {}; +// +// } +// } +llvm::SmallString<128> +getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) { + llvm::SmallString<128> Path; + for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R) + llvm::sys::path::append(Path, R->Name); + return Path; +} + +llvm::SmallString<128> getInfoRelativePath(const Decl *D) { + llvm::SmallVector<Reference, 4> Namespaces; + // The third arg in populateParentNamespaces is a boolean passed by reference, + // its value is not relevant in here so it's not used anywhere besides the + // function call + bool B = true; + populateParentNamespaces(Namespaces, D, B); + return getInfoRelativePath(Namespaces); +} + +class ClangDocCommentVisitor + : public ConstCommentVisitor<ClangDocCommentVisitor> { +public: + ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {} + + void parseComment(const comments::Comment *C); + + void visitTextComment(const TextComment *C); + void visitInlineCommandComment(const InlineCommandComment *C); + void visitHTMLStartTagComment(const HTMLStartTagComment *C); + void visitHTMLEndTagComment(const HTMLEndTagComment *C); + void visitBlockCommandComment(const BlockCommandComment *C); + void visitParamCommandComment(const ParamCommandComment *C); + void visitTParamCommandComment(const TParamCommandComment *C); + void visitVerbatimBlockComment(const VerbatimBlockComment *C); + void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); + void visitVerbatimLineComment(const VerbatimLineComment *C); + +private: + std::string getCommandName(unsigned CommandID) const; + bool isWhitespaceOnly(StringRef S) const; + + CommentInfo &CurrentCI; +}; + +void ClangDocCommentVisitor::parseComment(const comments::Comment *C) { + CurrentCI.Kind = C->getCommentKindName(); + ConstCommentVisitor<ClangDocCommentVisitor>::visit(C); + for (comments::Comment *Child : + llvm::make_range(C->child_begin(), C->child_end())) { + CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>()); + ClangDocCommentVisitor Visitor(*CurrentCI.Children.back()); + Visitor.parseComment(Child); + } +} + +void ClangDocCommentVisitor::visitTextComment(const TextComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI.Text = C->getText(); +} + +void ClangDocCommentVisitor::visitInlineCommandComment( + const InlineCommandComment *C) { + CurrentCI.Name = getCommandName(C->getCommandID()); + for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I) + CurrentCI.Args.push_back(C->getArgText(I)); +} + +void ClangDocCommentVisitor::visitHTMLStartTagComment( + const HTMLStartTagComment *C) { + CurrentCI.Name = C->getTagName(); + CurrentCI.SelfClosing = C->isSelfClosing(); + for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) { + const HTMLStartTagComment::Attribute &Attr = C->getAttr(I); + CurrentCI.AttrKeys.push_back(Attr.Name); + CurrentCI.AttrValues.push_back(Attr.Value); + } +} + +void ClangDocCommentVisitor::visitHTMLEndTagComment( + const HTMLEndTagComment *C) { + CurrentCI.Name = C->getTagName(); + CurrentCI.SelfClosing = true; +} + +void ClangDocCommentVisitor::visitBlockCommandComment( + const BlockCommandComment *C) { + CurrentCI.Name = getCommandName(C->getCommandID()); + for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I) + CurrentCI.Args.push_back(C->getArgText(I)); +} + +void ClangDocCommentVisitor::visitParamCommandComment( + const ParamCommandComment *C) { + CurrentCI.Direction = + ParamCommandComment::getDirectionAsString(C->getDirection()); + CurrentCI.Explicit = C->isDirectionExplicit(); + if (C->hasParamName()) + CurrentCI.ParamName = C->getParamNameAsWritten(); +} + +void ClangDocCommentVisitor::visitTParamCommandComment( + const TParamCommandComment *C) { + if (C->hasParamName()) + CurrentCI.ParamName = C->getParamNameAsWritten(); +} + +void ClangDocCommentVisitor::visitVerbatimBlockComment( + const VerbatimBlockComment *C) { + CurrentCI.Name = getCommandName(C->getCommandID()); + CurrentCI.CloseName = C->getCloseName(); +} + +void ClangDocCommentVisitor::visitVerbatimBlockLineComment( + const VerbatimBlockLineComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI.Text = C->getText(); +} + +void ClangDocCommentVisitor::visitVerbatimLineComment( + const VerbatimLineComment *C) { + if (!isWhitespaceOnly(C->getText())) + CurrentCI.Text = C->getText(); +} + +bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const { + return std::all_of(S.begin(), S.end(), isspace); +} + +std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { + const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); + if (Info) + return Info->Name; + // TODO: Add parsing for \file command. + return "<not a builtin command>"; +} + +// Serializing functions. + +template <typename T> static std::string serialize(T &I) { + SmallString<2048> Buffer; + llvm::BitstreamWriter Stream(Buffer); + ClangDocBitcodeWriter Writer(Stream); + Writer.emitBlock(I); + return Buffer.str().str(); +} + +std::string serialize(std::unique_ptr<Info> &I) { + switch (I->IT) { + case InfoType::IT_namespace: + return serialize(*static_cast<NamespaceInfo *>(I.get())); + case InfoType::IT_record: + return serialize(*static_cast<RecordInfo *>(I.get())); + case InfoType::IT_enum: + return serialize(*static_cast<EnumInfo *>(I.get())); + case InfoType::IT_function: + return serialize(*static_cast<FunctionInfo *>(I.get())); + default: + return ""; + } +} + +static void parseFullComment(const FullComment *C, CommentInfo &CI) { + ClangDocCommentVisitor Visitor(CI); + Visitor.parseComment(C); +} + +static SymbolID getUSRForDecl(const Decl *D) { + llvm::SmallString<128> USR; + if (index::generateUSRForDecl(D, USR)) + return SymbolID(); + return hashUSR(USR); +} + +static RecordDecl *getDeclForType(const QualType &T) { + if (const RecordDecl *D = T->getAsRecordDecl()) + return D->getDefinition(); + return nullptr; +} + +static bool isPublic(const clang::AccessSpecifier AS, + const clang::Linkage Link) { + if (AS == clang::AccessSpecifier::AS_private) + return false; + else if ((Link == clang::Linkage::ModuleLinkage) || + (Link == clang::Linkage::ExternalLinkage)) + return true; + return false; // otherwise, linkage is some form of internal linkage +} + +static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace, + const NamedDecl *D) { + bool IsAnonymousNamespace = false; + if (const auto *N = dyn_cast<NamespaceDecl>(D)) + IsAnonymousNamespace = N->isAnonymousNamespace(); + return !PublicOnly || + (!IsInAnonymousNamespace && !IsAnonymousNamespace && + isPublic(D->getAccessUnsafe(), D->getLinkageInternal())); +} + +// There are two uses for this function. +// 1) Getting the resulting mode of inheritance of a record. +// Example: class A {}; class B : private A {}; class C : public B {}; +// It's explicit that C is publicly inherited from C and B is privately +// inherited from A. It's not explicit but C is also privately inherited from +// A. This is the AS that this function calculates. FirstAS is the +// inheritance mode of `class C : B` and SecondAS is the inheritance mode of +// `class B : A`. +// 2) Getting the inheritance mode of an inherited attribute / method. +// Example : class A { public: int M; }; class B : private A {}; +// Class B is inherited from class A, which has a public attribute. This +// attribute is now part of the derived class B but it's not public. This +// will be private because the inheritance is private. This is the AS that +// this function calculates. FirstAS is the inheritance mode and SecondAS is +// the AS of the attribute / method. +static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS, + AccessSpecifier SecondAS) { + if (FirstAS == AccessSpecifier::AS_none || + SecondAS == AccessSpecifier::AS_none) + return AccessSpecifier::AS_none; + if (FirstAS == AccessSpecifier::AS_private || + SecondAS == AccessSpecifier::AS_private) + return AccessSpecifier::AS_private; + if (FirstAS == AccessSpecifier::AS_protected || + SecondAS == AccessSpecifier::AS_protected) + return AccessSpecifier::AS_protected; + return AccessSpecifier::AS_public; +} + +// The Access parameter is only provided when parsing the field of an inherited +// record, the access specification of the field depends on the inheritance mode +static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, + AccessSpecifier Access = AccessSpecifier::AS_public) { + for (const FieldDecl *F : D->fields()) { + if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) + continue; + if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) { + // Use getAccessUnsafe so that we just get the default AS_none if it's not + // valid, as opposed to an assert. + if (const auto *N = dyn_cast<EnumDecl>(T)) { + I.Members.emplace_back( + getUSRForDecl(T), N->getNameAsString(), InfoType::IT_enum, + getInfoRelativePath(N), F->getNameAsString(), + getFinalAccessSpecifier(Access, N->getAccessUnsafe())); + continue; + } else if (const auto *N = dyn_cast<RecordDecl>(T)) { + I.Members.emplace_back( + getUSRForDecl(T), N->getNameAsString(), InfoType::IT_record, + getInfoRelativePath(N), F->getNameAsString(), + getFinalAccessSpecifier(Access, N->getAccessUnsafe())); + continue; + } + } + I.Members.emplace_back( + F->getTypeSourceInfo()->getType().getAsString(), F->getNameAsString(), + getFinalAccessSpecifier(Access, F->getAccessUnsafe())); + } +} + +static void parseEnumerators(EnumInfo &I, const EnumDecl *D) { + for (const EnumConstantDecl *E : D->enumerators()) + I.Members.emplace_back(E->getNameAsString()); +} + +static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { + for (const ParmVarDecl *P : D->parameters()) { + if (const auto *T = getDeclForType(P->getOriginalType())) { + if (const auto *N = dyn_cast<EnumDecl>(T)) { + I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), + InfoType::IT_enum, getInfoRelativePath(N), + P->getNameAsString()); + continue; + } else if (const auto *N = dyn_cast<RecordDecl>(T)) { + I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(), + InfoType::IT_record, getInfoRelativePath(N), + P->getNameAsString()); + continue; + } + } + I.Params.emplace_back(P->getOriginalType().getAsString(), + P->getNameAsString()); + } +} + +// TODO: Remove the serialization of Parents and VirtualParents, this +// information is also extracted in the other definition of parseBases. +static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { + // Don't parse bases if this isn't a definition. + if (!D->isThisDeclarationADefinition()) + return; + for (const CXXBaseSpecifier &B : D->bases()) { + if (B.isVirtual()) + continue; + if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { + const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); + I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(), + InfoType::IT_record); + } else if (const RecordDecl *P = getDeclForType(B.getType())) + I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), + InfoType::IT_record, getInfoRelativePath(P)); + else + I.Parents.emplace_back(B.getType().getAsString()); + } + for (const CXXBaseSpecifier &B : D->vbases()) { + if (const auto *P = getDeclForType(B.getType())) + I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(), + InfoType::IT_record, + getInfoRelativePath(P)); + else + I.VirtualParents.emplace_back(B.getType().getAsString()); + } +} + +template <typename T> +static void +populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, + const T *D, bool &IsInAnonymousNamespace) { + const auto *DC = dyn_cast<DeclContext>(D); + while ((DC = DC->getParent())) { + if (const auto *N = dyn_cast<NamespaceDecl>(DC)) { + std::string Namespace; + if (N->isAnonymousNamespace()) { + Namespace = "@nonymous_namespace"; + IsInAnonymousNamespace = true; + } else + Namespace = N->getNameAsString(); + Namespaces.emplace_back(getUSRForDecl(N), Namespace, + InfoType::IT_namespace); + } else if (const auto *N = dyn_cast<RecordDecl>(DC)) + Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), + InfoType::IT_record); + else if (const auto *N = dyn_cast<FunctionDecl>(DC)) + Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), + InfoType::IT_function); + else if (const auto *N = dyn_cast<EnumDecl>(DC)) + Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), + InfoType::IT_enum); + } + // The global namespace should be added to the list of namespaces if the decl + // corresponds to a Record and if it doesn't have any namespace (because this + // means it's in the global namespace). Also if its outermost namespace is a + // record because that record matches the previous condition mentioned. + if ((Namespaces.empty() && dyn_cast<RecordDecl>(D)) || + (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record)) + Namespaces.emplace_back(SymbolID(), "GlobalNamespace", + InfoType::IT_namespace); +} + +template <typename T> +static void populateInfo(Info &I, const T *D, const FullComment *C, + bool &IsInAnonymousNamespace) { + I.USR = getUSRForDecl(D); + I.Name = D->getNameAsString(); + populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace); + if (C) { + I.Description.emplace_back(); + parseFullComment(C, I.Description.back()); + } +} + +template <typename T> +static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, + int LineNumber, StringRef Filename, + bool IsFileInRootDir, + bool &IsInAnonymousNamespace) { + populateInfo(I, D, C, IsInAnonymousNamespace); + if (D->isThisDeclarationADefinition()) + I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir); + else + I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir); +} + +static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, + const FullComment *FC, int LineNumber, + StringRef Filename, bool IsFileInRootDir, + bool &IsInAnonymousNamespace) { + populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir, + IsInAnonymousNamespace); + if (const auto *T = getDeclForType(D->getReturnType())) { + if (dyn_cast<EnumDecl>(T)) + I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), + InfoType::IT_enum, getInfoRelativePath(T)); + else if (dyn_cast<RecordDecl>(T)) + I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), + InfoType::IT_record, getInfoRelativePath(T)); + } else { + I.ReturnType = TypeInfo(D->getReturnType().getAsString()); + } + parseParameters(I, D); +} + +static void +parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, + bool PublicOnly, bool IsParent, + AccessSpecifier ParentAccess = AccessSpecifier::AS_public) { + // Don't parse bases if this isn't a definition. + if (!D->isThisDeclarationADefinition()) + return; + for (const CXXBaseSpecifier &B : D->bases()) { + if (const RecordType *Ty = B.getType()->getAs<RecordType>()) { + if (const CXXRecordDecl *Base = + cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) { + // Initialized without USR and name, this will be set in the following + // if-else stmt. + BaseRecordInfo BI( + {}, "", getInfoRelativePath(Base), B.isVirtual(), + getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()), + IsParent); + if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { + const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); + BI.USR = getUSRForDecl(D); + BI.Name = B.getType().getAsString(); + } else { + BI.USR = getUSRForDecl(Base); + BI.Name = Base->getNameAsString(); + } + parseFields(BI, Base, PublicOnly, BI.Access); + for (const auto &Decl : Base->decls()) + if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) { + // Don't serialize private methods + if (MD->getAccessUnsafe() == AccessSpecifier::AS_private || + !MD->isUserProvided()) + continue; + FunctionInfo FI; + FI.IsMethod = true; + // The seventh arg in populateFunctionInfo is a boolean passed by + // reference, its value is not relevant in here so it's not used + // anywhere besides the function call. + bool IsInAnonymousNamespace; + populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{}, + /*FileName=*/{}, IsFileInRootDir, + IsInAnonymousNamespace); + FI.Access = + getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe()); + BI.ChildFunctions.emplace_back(std::move(FI)); + } + I.Bases.emplace_back(std::move(BI)); + // Call this function recursively to get the inherited classes of + // this base; these new bases will also get stored in the original + // RecordInfo: I. + parseBases(I, Base, IsFileInRootDir, PublicOnly, false, + I.Bases.back().Access); + } + } + } +} + +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> +emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { + auto I = std::make_unique<NamespaceInfo>(); + bool IsInAnonymousNamespace = false; + populateInfo(*I, D, FC, IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + I->Name = D->isAnonymousNamespace() + ? llvm::SmallString<16>("@nonymous_namespace") + : I->Name; + I->Path = getInfoRelativePath(I->Namespace); + if (I->Namespace.empty() && I->USR == SymbolID()) + return {std::unique_ptr<Info>{std::move(I)}, nullptr}; + + auto ParentI = std::make_unique<NamespaceInfo>(); + ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR; + ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace, + getInfoRelativePath(I->Namespace)); + if (I->Namespace.empty()) + ParentI->Path = getInfoRelativePath(ParentI->Namespace); + return {std::unique_ptr<Info>{std::move(I)}, + std::unique_ptr<Info>{std::move(ParentI)}}; +} + +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> +emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { + auto I = std::make_unique<RecordInfo>(); + bool IsInAnonymousNamespace = false; + populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir, + IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + I->TagType = D->getTagKind(); + parseFields(*I, D, PublicOnly); + if (const auto *C = dyn_cast<CXXRecordDecl>(D)) { + if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) { + I->Name = TD->getNameAsString(); + I->IsTypeDef = true; + } + // TODO: remove first call to parseBases, that function should be deleted + parseBases(*I, C); + parseBases(*I, C, IsFileInRootDir, PublicOnly, true); + } + I->Path = getInfoRelativePath(I->Namespace); + + switch (I->Namespace[0].RefType) { + case InfoType::IT_namespace: { + auto ParentI = std::make_unique<NamespaceInfo>(); + ParentI->USR = I->Namespace[0].USR; + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, + getInfoRelativePath(I->Namespace)); + return {std::unique_ptr<Info>{std::move(I)}, + std::unique_ptr<Info>{std::move(ParentI)}}; + } + case InfoType::IT_record: { + auto ParentI = std::make_unique<RecordInfo>(); + ParentI->USR = I->Namespace[0].USR; + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, + getInfoRelativePath(I->Namespace)); + return {std::unique_ptr<Info>{std::move(I)}, + std::unique_ptr<Info>{std::move(ParentI)}}; + } + default: + llvm_unreachable("Invalid reference type for parent namespace"); + } +} + +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> +emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { + FunctionInfo Func; + bool IsInAnonymousNamespace = false; + populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, + IsInAnonymousNamespace); + Func.Access = clang::AccessSpecifier::AS_none; + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + // Wrap in enclosing scope + auto ParentI = std::make_unique<NamespaceInfo>(); + if (!Func.Namespace.empty()) + ParentI->USR = Func.Namespace[0].USR; + else + ParentI->USR = SymbolID(); + if (Func.Namespace.empty()) + ParentI->Path = getInfoRelativePath(ParentI->Namespace); + ParentI->ChildFunctions.emplace_back(std::move(Func)); + // Info is wrapped in its parent scope so it's returned in the second position + return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; +} + +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> +emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { + FunctionInfo Func; + bool IsInAnonymousNamespace = false; + populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir, + IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + Func.IsMethod = true; + + const NamedDecl *Parent = nullptr; + if (const auto *SD = + dyn_cast<ClassTemplateSpecializationDecl>(D->getParent())) + Parent = SD->getSpecializedTemplate(); + else + Parent = D->getParent(); + + SymbolID ParentUSR = getUSRForDecl(Parent); + Func.Parent = + Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record}; + Func.Access = D->getAccess(); + + // Wrap in enclosing scope + auto ParentI = std::make_unique<RecordInfo>(); + ParentI->USR = ParentUSR; + ParentI->ChildFunctions.emplace_back(std::move(Func)); + // Info is wrapped in its parent scope so it's returned in the second position + return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; +} + +std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> +emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) { + EnumInfo Enum; + bool IsInAnonymousNamespace = false; + populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir, + IsInAnonymousNamespace); + if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) + return {}; + + Enum.Scoped = D->isScoped(); + parseEnumerators(Enum, D); + + // Put in global namespace + if (Enum.Namespace.empty()) { + auto ParentI = std::make_unique<NamespaceInfo>(); + ParentI->USR = SymbolID(); + ParentI->ChildEnums.emplace_back(std::move(Enum)); + ParentI->Path = getInfoRelativePath(ParentI->Namespace); + // Info is wrapped in its parent scope so it's returned in the second + // position + return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; + } + + // Wrap in enclosing scope + switch (Enum.Namespace[0].RefType) { + case InfoType::IT_namespace: { + auto ParentI = std::make_unique<NamespaceInfo>(); + ParentI->USR = Enum.Namespace[0].USR; + ParentI->ChildEnums.emplace_back(std::move(Enum)); + // Info is wrapped in its parent scope so it's returned in the second + // position + return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; + } + case InfoType::IT_record: { + auto ParentI = std::make_unique<RecordInfo>(); + ParentI->USR = Enum.Namespace[0].USR; + ParentI->ChildEnums.emplace_back(std::move(Enum)); + // Info is wrapped in its parent scope so it's returned in the second + // position + return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}}; + } + default: + llvm_unreachable("Invalid reference type for parent namespace"); + } +} + +} // namespace serialize +} // namespace doc +} // namespace clang