//===-- 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 "clang/Lex/Lexer.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);

static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);

// 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 {
// namespace 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 llvm::all_of(S, 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.

std::string getSourceCode(const Decl *D, const SourceRange &R) {
  return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
                              D->getASTContext().getSourceManager(),
                              D->getASTContext().getLangOpts())
      .str();
}

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 TagDecl *getTagDeclForType(const QualType &T) {
  if (const TagDecl *D = T->getAsTagDecl())
    return D->getDefinition();
  return nullptr;
}

static RecordDecl *getRecordDeclForType(const QualType &T) {
  if (const RecordDecl *D = T->getAsRecordDecl())
    return D->getDefinition();
  return nullptr;
}

TypeInfo getTypeInfoForType(const QualType &T) {
  const TagDecl *TD = getTagDeclForType(T);
  if (!TD)
    return TypeInfo(Reference(SymbolID(), T.getAsString()));

  InfoType IT;
  if (dyn_cast<EnumDecl>(TD)) {
    IT = InfoType::IT_enum;
  } else if (dyn_cast<RecordDecl>(TD)) {
    IT = InfoType::IT_record;
  } else {
    IT = InfoType::IT_default;
  }
  return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
                            T.getAsString(), getInfoRelativePath(TD)));
}

static bool isPublic(const clang::AccessSpecifier AS,
                     const clang::Linkage Link) {
  if (AS == clang::AccessSpecifier::AS_private)
    return false;
  else if ((Link == clang::Linkage::Module) ||
           (Link == clang::Linkage::External))
    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()));
}

// The InsertChild functions insert the given info into the given scope using
// the method appropriate for that type. Some types are moved into the
// appropriate vector, while other types have Reference objects generated to
// refer to them.
//
// See MakeAndInsertIntoParent().
static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
  Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
                                Info.Name, getInfoRelativePath(Info.Namespace));
}

static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
  Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
                             Info.Name, getInfoRelativePath(Info.Namespace));
}

static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
  Scope.Enums.push_back(std::move(Info));
}

static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
  Scope.Functions.push_back(std::move(Info));
}

static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
  Scope.Typedefs.push_back(std::move(Info));
}

// Creates a parent of the correct type for the given child and inserts it into
// that parent.
//
// This is complicated by the fact that namespaces and records are inserted by
// reference (constructing a "Reference" object with that namespace/record's
// info), while everything else is inserted by moving it directly into the child
// vectors.
//
// For namespaces and records, explicitly specify a const& template parameter
// when invoking this function:
//   MakeAndInsertIntoParent<const Record&>(...);
// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
// parameter. Since each variant is used once, it's not worth having a more
// elaborate system to automatically deduce this information.
template <typename ChildType>
std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
  if (Child.Namespace.empty()) {
    // Insert into unnamed parent namespace.
    auto ParentNS = std::make_unique<NamespaceInfo>();
    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
    return ParentNS;
  }

  switch (Child.Namespace[0].RefType) {
  case InfoType::IT_namespace: {
    auto ParentNS = std::make_unique<NamespaceInfo>();
    ParentNS->USR = Child.Namespace[0].USR;
    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
    return ParentNS;
  }
  case InfoType::IT_record: {
    auto ParentRec = std::make_unique<RecordInfo>();
    ParentRec->USR = Child.Namespace[0].USR;
    InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
    return ParentRec;
  }
  default:
    llvm_unreachable("Invalid reference type for parent namespace");
  }
}

// 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;

    // Use getAccessUnsafe so that we just get the default AS_none if it's not
    // valid, as opposed to an assert.
    MemberTypeInfo &NewMember = I.Members.emplace_back(
        getTypeInfoForType(F->getTypeSourceInfo()->getType()),
        F->getNameAsString(),
        getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
    populateMemberTypeInfo(NewMember, F);
  }
}

static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
  for (const EnumConstantDecl *E : D->enumerators()) {
    std::string ValueExpr;
    if (const Expr *InitExpr = E->getInitExpr())
      ValueExpr = getSourceCode(D, InitExpr->getSourceRange());

    SmallString<16> ValueStr;
    E->getInitVal().toString(ValueStr);
    I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
  }
}

static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
  for (const ParmVarDecl *P : D->parameters()) {
    FieldTypeInfo &FieldInfo = I.Params.emplace_back(
        getTypeInfoForType(P->getOriginalType()), P->getNameAsString());
    FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
  }
}

// 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, B.getType().getAsString());
    } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
      I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
                             InfoType::IT_record, P->getQualifiedNameAsString(),
                             getInfoRelativePath(P));
    else
      I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
  }
  for (const CXXBaseSpecifier &B : D->vbases()) {
    if (const RecordDecl *P = getRecordDeclForType(B.getType()))
      I.VirtualParents.emplace_back(
          getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
          P->getQualifiedNameAsString(), getInfoRelativePath(P));
    else
      I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
  }
}

template <typename T>
static void
populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
                         const T *D, bool &IsInAnonymousNamespace) {
  const DeclContext *DC = D->getDeclContext();
  do {
    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,
                              N->getQualifiedNameAsString());
    } else if (const auto *N = dyn_cast<RecordDecl>(DC))
      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
                              InfoType::IT_record,
                              N->getQualifiedNameAsString());
    else if (const auto *N = dyn_cast<FunctionDecl>(DC))
      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
                              InfoType::IT_function,
                              N->getQualifiedNameAsString());
    else if (const auto *N = dyn_cast<EnumDecl>(DC))
      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
                              InfoType::IT_enum, N->getQualifiedNameAsString());
  } while ((DC = DC->getParent()));
  // 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() && isa<RecordDecl>(D)) ||
      (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
    Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
                            InfoType::IT_namespace);
}

void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
                                const clang::Decl *D) {
  if (const TemplateParameterList *ParamList =
          D->getDescribedTemplateParams()) {
    if (!TemplateInfo) {
      TemplateInfo.emplace();
    }
    for (const NamedDecl *ND : *ParamList) {
      TemplateInfo->Params.emplace_back(
          getSourceCode(ND, ND->getSourceRange()));
    }
  }
}

TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
                                         const TemplateArgument &Arg) {
  // The TemplateArgument's pretty printing handles all the normal cases
  // well enough for our requirements.
  std::string Str;
  llvm::raw_string_ostream Stream(Str);
  Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
  return TemplateParamInfo(Str);
}

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);
  I.ReturnType = getTypeInfoForType(D->getReturnType());
  parseParameters(I, D);

  PopulateTemplateParameters(I.Template, D);

  // Handle function template specializations.
  if (const FunctionTemplateSpecializationInfo *FTSI =
          D->getTemplateSpecializationInfo()) {
    if (!I.Template)
      I.Template.emplace();
    I.Template->Specialization.emplace();
    auto &Specialization = *I.Template->Specialization;

    Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());

    // Template parameters to the specialization.
    if (FTSI->TemplateArguments) {
      for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
        Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
      }
    }
  }
}

static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
  assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");

  ASTContext& Context = D->getASTContext();
  // TODO investigate whether we can use ASTContext::getCommentForDecl instead
  // of this logic. See also similar code in Mapper.cpp.
  RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
  if (!Comment)
    return;

  Comment->setAttached();
  if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
    I.Description.emplace_back();
    parseFullComment(fc, I.Description.back());
  }
}

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.Children.Functions.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};

  // Namespaces are inserted into the parent by reference, so we need to return
  // both the parent and the record itself.
  return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
}

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);

  PopulateTemplateParameters(I->Template, D);

  // Full and partial specializations.
  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
    if (!I->Template)
      I->Template.emplace();
    I->Template->Specialization.emplace();
    auto &Specialization = *I->Template->Specialization;

    // What this is a specialization of.
    auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
    if (SpecOf.is<ClassTemplateDecl *>()) {
      Specialization.SpecializationOf =
          getUSRForDecl(SpecOf.get<ClassTemplateDecl *>());
    } else if (SpecOf.is<ClassTemplatePartialSpecializationDecl *>()) {
      Specialization.SpecializationOf =
          getUSRForDecl(SpecOf.get<ClassTemplatePartialSpecializationDecl *>());
    }

    // Parameters to the specilization. For partial specializations, get the
    // parameters "as written" from the ClassTemplatePartialSpecializationDecl
    // because the non-explicit template parameters will have generated internal
    // placeholder names rather than the names the user typed that match the
    // template parameters.
    if (const ClassTemplatePartialSpecializationDecl *CTPSD =
            dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
      if (const ASTTemplateArgumentListInfo *AsWritten =
              CTPSD->getTemplateArgsAsWritten()) {
        for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
          Specialization.Params.emplace_back(
              getSourceCode(D, (*AsWritten)[i].getSourceRange()));
        }
      }
    } else {
      for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
        Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
      }
    }
  }

  // Records are inserted into the parent by reference, so we need to return
  // both the parent and the record itself.
  auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
  return {std::move(I), std::move(Parent)};
}

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 {};

  // Info is wrapped in its parent scope so is returned in the second position.
  return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
}

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,
                Parent->getQualifiedNameAsString()};
  Func.Access = D->getAccess();

  // Info is wrapped in its parent scope so is returned in the second position.
  return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
}

std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
  TypedefInfo Info;

  bool IsInAnonymousNamespace = false;
  populateInfo(Info, D, FC, IsInAnonymousNamespace);
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
    return {};

  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
  Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
  if (Info.Underlying.Type.Name.empty()) {
    // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
    // The record serializer explicitly checks for this syntax and constructs
    // a record with that name, so we don't want to emit a duplicate here.
    return {};
  }
  Info.IsUsing = false;

  // Info is wrapped in its parent scope so is returned in the second position.
  return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
}

// A type alias is a C++ "using" declaration for a type. It gets mapped to a
// TypedefInfo with the IsUsing flag set.
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
  TypedefInfo Info;

  bool IsInAnonymousNamespace = false;
  populateInfo(Info, D, FC, IsInAnonymousNamespace);
  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
    return {};

  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
  Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
  Info.IsUsing = true;

  // Info is wrapped in its parent scope so is returned in the second position.
  return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
}

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();
  if (D->isFixed()) {
    auto Name = D->getIntegerType().getAsString();
    Enum.BaseType = TypeInfo(Name, Name);
  }
  parseEnumerators(Enum, D);

  // Info is wrapped in its parent scope so is returned in the second position.
  return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
}

} // namespace serialize
} // namespace doc
} // namespace clang
