brintos

brintos / llvm-project-archived public Read only

0
0
Text · 44.6 KiB · e1d6057 Raw
1221 lines · cpp
1//===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Serialize.h"10#include "BitcodeWriter.h"11 12#include "clang/AST/Attr.h"13#include "clang/AST/Comment.h"14#include "clang/AST/CommentVisitor.h"15#include "clang/AST/DeclFriend.h"16#include "clang/AST/ExprConcepts.h"17#include "clang/AST/Mangle.h"18#include "clang/Index/USRGeneration.h"19#include "clang/Lex/Lexer.h"20#include "llvm/ADT/StringExtras.h"21#include "llvm/Support/SHA1.h"22 23using clang::comments::FullComment;24 25namespace clang {26namespace doc {27namespace serialize {28 29namespace {30static SmallString<16> exprToString(const clang::Expr *E) {31  clang::LangOptions Opts;32  clang::PrintingPolicy Policy(Opts);33  SmallString<16> Result;34  llvm::raw_svector_ostream OS(Result);35  E->printPretty(OS, nullptr, Policy);36  return Result;37}38} // namespace39 40SymbolID hashUSR(llvm::StringRef USR) {41  return llvm::SHA1::hash(arrayRefFromStringRef(USR));42}43 44template <typename T>45static void46populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,47                         const T *D, bool &IsAnonymousNamespace);48 49static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D);50static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,51                                   const DeclaratorDecl *D,52                                   bool IsStatic = false);53 54static void getTemplateParameters(const TemplateParameterList *TemplateParams,55                                  llvm::raw_ostream &Stream) {56  Stream << "template <";57 58  for (unsigned i = 0; i < TemplateParams->size(); ++i) {59    if (i > 0)60      Stream << ", ";61 62    const NamedDecl *Param = TemplateParams->getParam(i);63    if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Param)) {64      if (TTP->wasDeclaredWithTypename())65        Stream << "typename";66      else67        Stream << "class";68      if (TTP->isParameterPack())69        Stream << "...";70      Stream << " " << TTP->getNameAsString();71 72      // We need to also handle type constraints for code like:73      //   template <class T = void>74      //   class C {};75      if (TTP->hasTypeConstraint()) {76        Stream << " = ";77        TTP->getTypeConstraint()->print(78            Stream, TTP->getASTContext().getPrintingPolicy());79      }80    } else if (const auto *NTTP =81                   llvm::dyn_cast<NonTypeTemplateParmDecl>(Param)) {82      NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy());83      if (NTTP->isParameterPack())84        Stream << "...";85      Stream << " " << NTTP->getNameAsString();86    } else if (const auto *TTPD =87                   llvm::dyn_cast<TemplateTemplateParmDecl>(Param)) {88      Stream << "template <";89      getTemplateParameters(TTPD->getTemplateParameters(), Stream);90      Stream << "> class " << TTPD->getNameAsString();91    }92  }93 94  Stream << "> ";95}96 97// Extract the full function prototype from a FunctionDecl including98// Full Decl99static llvm::SmallString<256>100getFunctionPrototype(const FunctionDecl *FuncDecl) {101  llvm::SmallString<256> Result;102  llvm::raw_svector_ostream Stream(Result);103  const ASTContext &Ctx = FuncDecl->getASTContext();104  const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl);105  // If it's a templated function, handle the template parameters106  if (const auto *TmplDecl = FuncDecl->getDescribedTemplate())107    getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);108 109  // If it's a virtual method110  if (Method && Method->isVirtual())111    Stream << "virtual ";112 113  // Print return type114  FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy());115 116  // Print function name117  Stream << " " << FuncDecl->getNameAsString() << "(";118 119  // Print parameter list with types, names, and default values120  for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) {121    if (I > 0)122      Stream << ", ";123    const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(I);124    QualType ParamType = ParamDecl->getType();125    ParamType.print(Stream, Ctx.getPrintingPolicy());126 127    // Print parameter name if it has one128    if (!ParamDecl->getName().empty())129      Stream << " " << ParamDecl->getNameAsString();130 131    // Print default argument if it exists132    if (ParamDecl->hasDefaultArg() &&133        !ParamDecl->hasUninstantiatedDefaultArg()) {134      if (const Expr *DefaultArg = ParamDecl->getDefaultArg()) {135        Stream << " = ";136        DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy());137      }138    }139  }140 141  // If it is a variadic function, add '...'142  if (FuncDecl->isVariadic()) {143    if (FuncDecl->getNumParams() > 0)144      Stream << ", ";145    Stream << "...";146  }147 148  Stream << ")";149 150  // If it's a const method, add 'const' qualifier151  if (Method) {152    if (Method->isDeleted())153      Stream << " = delete";154    if (Method->size_overridden_methods())155      Stream << " override";156    if (Method->hasAttr<clang::FinalAttr>())157      Stream << " final";158    if (Method->isConst())159      Stream << " const";160    if (Method->isPureVirtual())161      Stream << " = 0";162  }163 164  if (auto ExceptionSpecType = FuncDecl->getExceptionSpecType())165    Stream << " " << ExceptionSpecType;166 167  return Result; // Convert SmallString to std::string for return168}169 170static llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) {171  llvm::SmallString<16> Result;172  llvm::raw_svector_ostream Stream(Result);173  const ASTContext &Ctx = Alias->getASTContext();174  if (const auto *TmplDecl = Alias->getDescribedTemplate())175    getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);176  Stream << "using " << Alias->getNameAsString() << " = ";177  QualType Q = Alias->getUnderlyingType();178  Q.print(Stream, Ctx.getPrintingPolicy());179 180  return Result;181}182 183// A function to extract the appropriate relative path for a given info's184// documentation. The path returned is a composite of the parent namespaces.185//186// Example: Given the below, the directory path for class C info will be187// <root>/A/B188//189// namespace A {190// namespace B {191//192// class C {};193//194// }195// }196static llvm::SmallString<128>197getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {198  llvm::SmallString<128> Path;199  for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)200    llvm::sys::path::append(Path, R->Name);201  return Path;202}203 204static llvm::SmallString<128> getInfoRelativePath(const Decl *D) {205  llvm::SmallVector<Reference, 4> Namespaces;206  // The third arg in populateParentNamespaces is a boolean passed by reference,207  // its value is not relevant in here so it's not used anywhere besides the208  // function call209  bool B = true;210  populateParentNamespaces(Namespaces, D, B);211  return getInfoRelativePath(Namespaces);212}213 214class ClangDocCommentVisitor215    : public ConstCommentVisitor<ClangDocCommentVisitor> {216public:217  ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}218 219  void parseComment(const comments::Comment *C);220 221  void visitTextComment(const TextComment *C);222  void visitInlineCommandComment(const InlineCommandComment *C);223  void visitHTMLStartTagComment(const HTMLStartTagComment *C);224  void visitHTMLEndTagComment(const HTMLEndTagComment *C);225  void visitBlockCommandComment(const BlockCommandComment *C);226  void visitParamCommandComment(const ParamCommandComment *C);227  void visitTParamCommandComment(const TParamCommandComment *C);228  void visitVerbatimBlockComment(const VerbatimBlockComment *C);229  void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);230  void visitVerbatimLineComment(const VerbatimLineComment *C);231 232private:233  std::string getCommandName(unsigned CommandID) const;234  bool isWhitespaceOnly(StringRef S) const;235 236  CommentInfo &CurrentCI;237};238 239void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {240  CurrentCI.Kind = stringToCommentKind(C->getCommentKindName());241  ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);242  for (comments::Comment *Child :243       llvm::make_range(C->child_begin(), C->child_end())) {244    CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());245    ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());246    Visitor.parseComment(Child);247  }248}249 250void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {251  if (!isWhitespaceOnly(C->getText()))252    CurrentCI.Text = C->getText();253}254 255void ClangDocCommentVisitor::visitInlineCommandComment(256    const InlineCommandComment *C) {257  CurrentCI.Name = getCommandName(C->getCommandID());258  for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)259    CurrentCI.Args.push_back(C->getArgText(I));260}261 262void ClangDocCommentVisitor::visitHTMLStartTagComment(263    const HTMLStartTagComment *C) {264  CurrentCI.Name = C->getTagName();265  CurrentCI.SelfClosing = C->isSelfClosing();266  for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {267    const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);268    CurrentCI.AttrKeys.push_back(Attr.Name);269    CurrentCI.AttrValues.push_back(Attr.Value);270  }271}272 273void ClangDocCommentVisitor::visitHTMLEndTagComment(274    const HTMLEndTagComment *C) {275  CurrentCI.Name = C->getTagName();276  CurrentCI.SelfClosing = true;277}278 279void ClangDocCommentVisitor::visitBlockCommandComment(280    const BlockCommandComment *C) {281  CurrentCI.Name = getCommandName(C->getCommandID());282  for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)283    CurrentCI.Args.push_back(C->getArgText(I));284}285 286void ClangDocCommentVisitor::visitParamCommandComment(287    const ParamCommandComment *C) {288  CurrentCI.Direction =289      ParamCommandComment::getDirectionAsString(C->getDirection());290  CurrentCI.Explicit = C->isDirectionExplicit();291  if (C->hasParamName())292    CurrentCI.ParamName = C->getParamNameAsWritten();293}294 295void ClangDocCommentVisitor::visitTParamCommandComment(296    const TParamCommandComment *C) {297  if (C->hasParamName())298    CurrentCI.ParamName = C->getParamNameAsWritten();299}300 301void ClangDocCommentVisitor::visitVerbatimBlockComment(302    const VerbatimBlockComment *C) {303  CurrentCI.Name = getCommandName(C->getCommandID());304  CurrentCI.CloseName = C->getCloseName();305}306 307void ClangDocCommentVisitor::visitVerbatimBlockLineComment(308    const VerbatimBlockLineComment *C) {309  if (!isWhitespaceOnly(C->getText()))310    CurrentCI.Text = C->getText();311}312 313void ClangDocCommentVisitor::visitVerbatimLineComment(314    const VerbatimLineComment *C) {315  if (!isWhitespaceOnly(C->getText()))316    CurrentCI.Text = C->getText();317}318 319bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {320  return llvm::all_of(S, isspace);321}322 323std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {324  const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);325  if (Info)326    return Info->Name;327  // TODO: Add parsing for \file command.328  return "<not a builtin command>";329}330 331// Serializing functions.332 333static std::string getSourceCode(const Decl *D, const SourceRange &R) {334  return Lexer::getSourceText(CharSourceRange::getTokenRange(R),335                              D->getASTContext().getSourceManager(),336                              D->getASTContext().getLangOpts())337      .str();338}339 340template <typename T> static std::string serialize(T &I) {341  SmallString<2048> Buffer;342  llvm::BitstreamWriter Stream(Buffer);343  ClangDocBitcodeWriter Writer(Stream);344  Writer.emitBlock(I);345  return Buffer.str().str();346}347 348std::string serialize(std::unique_ptr<Info> &I) {349  switch (I->IT) {350  case InfoType::IT_namespace:351    return serialize(*static_cast<NamespaceInfo *>(I.get()));352  case InfoType::IT_record:353    return serialize(*static_cast<RecordInfo *>(I.get()));354  case InfoType::IT_enum:355    return serialize(*static_cast<EnumInfo *>(I.get()));356  case InfoType::IT_function:357    return serialize(*static_cast<FunctionInfo *>(I.get()));358  case InfoType::IT_concept:359    return serialize(*static_cast<ConceptInfo *>(I.get()));360  case InfoType::IT_variable:361    return serialize(*static_cast<VarInfo *>(I.get()));362  case InfoType::IT_friend:363  case InfoType::IT_typedef:364  case InfoType::IT_default:365    return "";366  }367  llvm_unreachable("unhandled enumerator");368}369 370static void parseFullComment(const FullComment *C, CommentInfo &CI) {371  ClangDocCommentVisitor Visitor(CI);372  Visitor.parseComment(C);373}374 375static SymbolID getUSRForDecl(const Decl *D) {376  llvm::SmallString<128> USR;377  if (index::generateUSRForDecl(D, USR))378    return SymbolID();379  return hashUSR(USR);380}381 382static TagDecl *getTagDeclForType(const QualType &T) {383  if (const TagDecl *D = T->getAsTagDecl())384    return D->getDefinition();385  return nullptr;386}387 388static RecordDecl *getRecordDeclForType(const QualType &T) {389  if (const RecordDecl *D = T->getAsRecordDecl())390    return D->getDefinition();391  return nullptr;392}393 394static TypeInfo getTypeInfoForType(const QualType &T,395                                   const PrintingPolicy &Policy) {396  const TagDecl *TD = getTagDeclForType(T);397  if (!TD) {398    TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));399    TI.IsBuiltIn = T->isBuiltinType();400    TI.IsTemplate = T->isTemplateTypeParmType();401    return TI;402  }403  InfoType IT;404  if (isa<EnumDecl>(TD)) {405    IT = InfoType::IT_enum;406  } else if (isa<RecordDecl>(TD)) {407    IT = InfoType::IT_record;408  } else {409    IT = InfoType::IT_default;410  }411  Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,412                          T.getAsString(Policy), getInfoRelativePath(TD));413  TypeInfo TI = TypeInfo(R);414  TI.IsBuiltIn = T->isBuiltinType();415  TI.IsTemplate = T->isTemplateTypeParmType();416  return TI;417}418 419static bool isPublic(const clang::AccessSpecifier AS,420                     const clang::Linkage Link) {421  if (AS == clang::AccessSpecifier::AS_private)422    return false;423  if ((Link == clang::Linkage::Module) || (Link == clang::Linkage::External))424    return true;425  return false; // otherwise, linkage is some form of internal linkage426}427 428static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,429                                const NamedDecl *D) {430  bool IsAnonymousNamespace = false;431  if (const auto *N = dyn_cast<NamespaceDecl>(D))432    IsAnonymousNamespace = N->isAnonymousNamespace();433  return !PublicOnly ||434         (!IsInAnonymousNamespace && !IsAnonymousNamespace &&435          isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));436}437 438// The InsertChild functions insert the given info into the given scope using439// the method appropriate for that type. Some types are moved into the440// appropriate vector, while other types have Reference objects generated to441// refer to them.442//443// See MakeAndInsertIntoParent().444static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {445  Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,446                                Info.Name, getInfoRelativePath(Info.Namespace));447}448 449static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {450  Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,451                             Info.Name, getInfoRelativePath(Info.Namespace),452                             Info.MangledName);453}454 455static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {456  Scope.Enums.push_back(std::move(Info));457}458 459static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {460  Scope.Functions.push_back(std::move(Info));461}462 463static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {464  Scope.Typedefs.push_back(std::move(Info));465}466 467static void InsertChild(ScopeChildren &Scope, ConceptInfo Info) {468  Scope.Concepts.push_back(std::move(Info));469}470 471static void InsertChild(ScopeChildren &Scope, VarInfo Info) {472  Scope.Variables.push_back(std::move(Info));473}474 475// Creates a parent of the correct type for the given child and inserts it into476// that parent.477//478// This is complicated by the fact that namespaces and records are inserted by479// reference (constructing a "Reference" object with that namespace/record's480// info), while everything else is inserted by moving it directly into the child481// vectors.482//483// For namespaces and records, explicitly specify a const& template parameter484// when invoking this function:485//   MakeAndInsertIntoParent<const Record&>(...);486// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the487// parameter. Since each variant is used once, it's not worth having a more488// elaborate system to automatically deduce this information.489template <typename ChildType>490static std::unique_ptr<Info> makeAndInsertIntoParent(ChildType Child) {491  if (Child.Namespace.empty()) {492    // Insert into unnamed parent namespace.493    auto ParentNS = std::make_unique<NamespaceInfo>();494    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));495    return ParentNS;496  }497 498  switch (Child.Namespace[0].RefType) {499  case InfoType::IT_namespace: {500    auto ParentNS = std::make_unique<NamespaceInfo>();501    ParentNS->USR = Child.Namespace[0].USR;502    InsertChild(ParentNS->Children, std::forward<ChildType>(Child));503    return ParentNS;504  }505  case InfoType::IT_record: {506    auto ParentRec = std::make_unique<RecordInfo>();507    ParentRec->USR = Child.Namespace[0].USR;508    InsertChild(ParentRec->Children, std::forward<ChildType>(Child));509    return ParentRec;510  }511  case InfoType::IT_default:512  case InfoType::IT_enum:513  case InfoType::IT_function:514  case InfoType::IT_typedef:515  case InfoType::IT_concept:516  case InfoType::IT_variable:517  case InfoType::IT_friend:518    break;519  }520  llvm_unreachable("Invalid reference type for parent namespace");521}522 523// There are two uses for this function.524// 1) Getting the resulting mode of inheritance of a record.525//    Example: class A {}; class B : private A {}; class C : public B {};526//    It's explicit that C is publicly inherited from C and B is privately527//    inherited from A. It's not explicit but C is also privately inherited from528//    A. This is the AS that this function calculates. FirstAS is the529//    inheritance mode of `class C : B` and SecondAS is the inheritance mode of530//    `class B : A`.531// 2) Getting the inheritance mode of an inherited attribute / method.532//    Example : class A { public: int M; }; class B : private A {};533//    Class B is inherited from class A, which has a public attribute. This534//    attribute is now part of the derived class B but it's not public. This535//    will be private because the inheritance is private. This is the AS that536//    this function calculates. FirstAS is the inheritance mode and SecondAS is537//    the AS of the attribute / method.538static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,539                                               AccessSpecifier SecondAS) {540  if (FirstAS == AccessSpecifier::AS_none ||541      SecondAS == AccessSpecifier::AS_none)542    return AccessSpecifier::AS_none;543  if (FirstAS == AccessSpecifier::AS_private ||544      SecondAS == AccessSpecifier::AS_private)545    return AccessSpecifier::AS_private;546  if (FirstAS == AccessSpecifier::AS_protected ||547      SecondAS == AccessSpecifier::AS_protected)548    return AccessSpecifier::AS_protected;549  return AccessSpecifier::AS_public;550}551 552// The Access parameter is only provided when parsing the field of an inherited553// record, the access specification of the field depends on the inheritance mode554static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,555                        AccessSpecifier Access = AccessSpecifier::AS_public) {556  for (const FieldDecl *F : D->fields()) {557    if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))558      continue;559    populateMemberTypeInfo(I, Access, F);560  }561  const auto *CxxRD = dyn_cast<CXXRecordDecl>(D);562  if (!CxxRD)563    return;564  for (Decl *CxxDecl : CxxRD->decls()) {565    auto *VD = dyn_cast<VarDecl>(CxxDecl);566    if (!VD ||567        !shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, VD))568      continue;569 570    if (VD->isStaticDataMember())571      populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true);572  }573}574 575static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {576  for (const EnumConstantDecl *E : D->enumerators()) {577    std::string ValueExpr;578    if (const Expr *InitExpr = E->getInitExpr())579      ValueExpr = getSourceCode(D, InitExpr->getSourceRange());580    SmallString<16> ValueStr;581    E->getInitVal().toString(ValueStr);582    I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);583    ASTContext &Context = E->getASTContext();584    if (RawComment *Comment =585            E->getASTContext().getRawCommentForDeclNoCache(E)) {586      Comment->setAttached();587      if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {588        EnumValueInfo &Member = I.Members.back();589        Member.Description.emplace_back();590        parseFullComment(Fc, Member.Description.back());591      }592    }593  }594}595 596static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {597  auto &LO = D->getLangOpts();598  for (const ParmVarDecl *P : D->parameters()) {599    FieldTypeInfo &FieldInfo = I.Params.emplace_back(600        getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());601    FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());602  }603}604 605// TODO: Remove the serialization of Parents and VirtualParents, this606// information is also extracted in the other definition of parseBases.607static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {608  // Don't parse bases if this isn't a definition.609  if (!D->isThisDeclarationADefinition())610    return;611 612  for (const CXXBaseSpecifier &B : D->bases()) {613    if (B.isVirtual())614      continue;615    if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {616      const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();617      I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),618                             InfoType::IT_record, B.getType().getAsString());619    } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))620      I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),621                             InfoType::IT_record, P->getQualifiedNameAsString(),622                             getInfoRelativePath(P));623    else624      I.Parents.emplace_back(SymbolID(), B.getType().getAsString());625  }626  for (const CXXBaseSpecifier &B : D->vbases()) {627    if (const RecordDecl *P = getRecordDeclForType(B.getType()))628      I.VirtualParents.emplace_back(629          getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,630          P->getQualifiedNameAsString(), getInfoRelativePath(P));631    else632      I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());633  }634}635 636template <typename T>637static void638populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,639                         const T *D, bool &IsInAnonymousNamespace) {640  const DeclContext *DC = D->getDeclContext();641  do {642    if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {643      std::string Namespace;644      if (N->isAnonymousNamespace()) {645        Namespace = "@nonymous_namespace";646        IsInAnonymousNamespace = true;647      } else648        Namespace = N->getNameAsString();649      Namespaces.emplace_back(getUSRForDecl(N), Namespace,650                              InfoType::IT_namespace,651                              N->getQualifiedNameAsString());652    } else if (const auto *N = dyn_cast<RecordDecl>(DC))653      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),654                              InfoType::IT_record,655                              N->getQualifiedNameAsString());656    else if (const auto *N = dyn_cast<FunctionDecl>(DC))657      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),658                              InfoType::IT_function,659                              N->getQualifiedNameAsString());660    else if (const auto *N = dyn_cast<EnumDecl>(DC))661      Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),662                              InfoType::IT_enum, N->getQualifiedNameAsString());663  } while ((DC = DC->getParent()));664  // The global namespace should be added to the list of namespaces if the decl665  // corresponds to a Record and if it doesn't have any namespace (because this666  // means it's in the global namespace). Also if its outermost namespace is a667  // record because that record matches the previous condition mentioned.668  if ((Namespaces.empty() && isa<RecordDecl>(D)) ||669      (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))670    Namespaces.emplace_back(SymbolID(), "GlobalNamespace",671                            InfoType::IT_namespace);672}673 674static void675populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,676                           const clang::Decl *D) {677  if (const TemplateParameterList *ParamList =678          D->getDescribedTemplateParams()) {679    if (!TemplateInfo) {680      TemplateInfo.emplace();681    }682    for (const NamedDecl *ND : *ParamList) {683      TemplateInfo->Params.emplace_back(684          getSourceCode(ND, ND->getSourceRange()));685    }686  }687}688 689static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,690                                                  const TemplateArgument &Arg) {691  // The TemplateArgument's pretty printing handles all the normal cases692  // well enough for our requirements.693  std::string Str;694  llvm::raw_string_ostream Stream(Str);695  Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);696  return TemplateParamInfo(Str);697}698 699template <typename T>700static void populateInfo(Info &I, const T *D, const FullComment *C,701                         bool &IsInAnonymousNamespace) {702  I.USR = getUSRForDecl(D);703  if (auto ConversionDecl = dyn_cast_or_null<CXXConversionDecl>(D);704      ConversionDecl && ConversionDecl->getConversionType()705                            .getTypePtr()706                            ->isTemplateTypeParmType())707    I.Name = "operator " + ConversionDecl->getConversionType().getAsString();708  else709    I.Name = D->getNameAsString();710  populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);711  if (C) {712    I.Description.emplace_back();713    parseFullComment(C, I.Description.back());714  }715}716 717template <typename T>718static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,719                               Location Loc, bool &IsInAnonymousNamespace) {720  populateInfo(I, D, C, IsInAnonymousNamespace);721  if (D->isThisDeclarationADefinition())722    I.DefLoc = Loc;723  else724    I.Loc.emplace_back(Loc);725 726  auto *Mangler = ItaniumMangleContext::create(727      D->getASTContext(), D->getASTContext().getDiagnostics());728  std::string MangledName;729  llvm::raw_string_ostream MangledStream(MangledName);730  if (auto *CXXD = dyn_cast<CXXRecordDecl>(D))731    Mangler->mangleCXXVTable(CXXD, MangledStream);732  else733    MangledStream << D->getNameAsString();734  // A 250 length limit was chosen since 255 is a common limit across735  // different filesystems, with a 5 character buffer for file extensions.736  if (MangledName.size() > 250) {737    auto SymbolID = llvm::toStringRef(llvm::toHex(I.USR)).str();738    I.MangledName = MangledName.substr(0, 250 - SymbolID.size()) + SymbolID;739  } else740    I.MangledName = MangledName;741  delete Mangler;742}743 744static void745handleCompoundConstraints(const Expr *Constraint,746                          std::vector<ConstraintInfo> &ConstraintInfos) {747  if (Constraint->getStmtClass() == Stmt::ParenExprClass) {748    handleCompoundConstraints(dyn_cast<ParenExpr>(Constraint)->getSubExpr(),749                              ConstraintInfos);750  } else if (Constraint->getStmtClass() == Stmt::BinaryOperatorClass) {751    auto *BinaryOpExpr = dyn_cast<BinaryOperator>(Constraint);752    handleCompoundConstraints(BinaryOpExpr->getLHS(), ConstraintInfos);753    handleCompoundConstraints(BinaryOpExpr->getRHS(), ConstraintInfos);754  } else if (Constraint->getStmtClass() ==755             Stmt::ConceptSpecializationExprClass) {756    auto *Concept = dyn_cast<ConceptSpecializationExpr>(Constraint);757    ConstraintInfo CI(getUSRForDecl(Concept->getNamedConcept()),758                      Concept->getNamedConcept()->getNameAsString());759    CI.ConstraintExpr = exprToString(Concept);760    ConstraintInfos.push_back(CI);761  }762}763 764static void populateConstraints(TemplateInfo &I, const TemplateDecl *D) {765  if (!D || !D->hasAssociatedConstraints())766    return;767 768  SmallVector<AssociatedConstraint> AssociatedConstraints;769  D->getAssociatedConstraints(AssociatedConstraints);770  for (const auto &Constraint : AssociatedConstraints) {771    if (!Constraint)772      continue;773 774    // TODO: Investigate if atomic constraints need to be handled specifically.775    if (const auto *ConstraintExpr =776            dyn_cast_or_null<ConceptSpecializationExpr>(777                Constraint.ConstraintExpr)) {778      ConstraintInfo CI(getUSRForDecl(ConstraintExpr->getNamedConcept()),779                        ConstraintExpr->getNamedConcept()->getNameAsString());780      CI.ConstraintExpr = exprToString(ConstraintExpr);781      I.Constraints.push_back(std::move(CI));782    } else {783      handleCompoundConstraints(Constraint.ConstraintExpr, I.Constraints);784    }785  }786}787 788static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,789                                 const FullComment *FC, Location Loc,790                                 bool &IsInAnonymousNamespace) {791  populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);792  auto &LO = D->getLangOpts();793  I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);794  I.Prototype = getFunctionPrototype(D);795  parseParameters(I, D);796  I.IsStatic = D->isStatic();797 798  populateTemplateParameters(I.Template, D);799  if (I.Template)800    populateConstraints(I.Template.value(), D->getDescribedFunctionTemplate());801 802  // Handle function template specializations.803  if (const FunctionTemplateSpecializationInfo *FTSI =804          D->getTemplateSpecializationInfo()) {805    if (!I.Template)806      I.Template.emplace();807    I.Template->Specialization.emplace();808    auto &Specialization = *I.Template->Specialization;809 810    Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());811 812    // Template parameters to the specialization.813    if (FTSI->TemplateArguments) {814      for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {815        Specialization.Params.push_back(convertTemplateArgToInfo(D, Arg));816      }817    }818  }819}820 821static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) {822  assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");823 824  ASTContext &Context = D->getASTContext();825  // TODO investigate whether we can use ASTContext::getCommentForDecl instead826  // of this logic. See also similar code in Mapper.cpp.827  RawComment *Comment = Context.getRawCommentForDeclNoCache(D);828  if (!Comment)829    return;830 831  Comment->setAttached();832  if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {833    I.Description.emplace_back();834    parseFullComment(Fc, I.Description.back());835  }836}837 838static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,839                                   const DeclaratorDecl *D, bool IsStatic) {840  // Use getAccessUnsafe so that we just get the default AS_none if it's not841  // valid, as opposed to an assert.842  MemberTypeInfo &NewMember = I.Members.emplace_back(843      getTypeInfoForType(D->getTypeSourceInfo()->getType(), D->getLangOpts()),844      D->getNameAsString(),845      getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic);846  populateMemberTypeInfo(NewMember, D);847}848 849static void850parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,851           bool PublicOnly, bool IsParent,852           AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {853  // Don't parse bases if this isn't a definition.854  if (!D->isThisDeclarationADefinition())855    return;856  for (const CXXBaseSpecifier &B : D->bases()) {857    if (const auto *Base = B.getType()->getAsCXXRecordDecl()) {858      if (Base->isCompleteDefinition()) {859        // Initialized without USR and name, this will be set in the following860        // if-else stmt.861        BaseRecordInfo BI(862            {}, "", getInfoRelativePath(Base), B.isVirtual(),863            getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),864            IsParent);865        if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {866          const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();867          BI.USR = getUSRForDecl(D);868          BI.Name = B.getType().getAsString();869        } else {870          BI.USR = getUSRForDecl(Base);871          BI.Name = Base->getNameAsString();872        }873        parseFields(BI, Base, PublicOnly, BI.Access);874        for (const auto &Decl : Base->decls())875          if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {876            // Don't serialize private methods877            if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||878                !MD->isUserProvided())879              continue;880            FunctionInfo FI;881            FI.IsMethod = true;882            FI.IsStatic = MD->isStatic();883            // The seventh arg in populateFunctionInfo is a boolean passed by884            // reference, its value is not relevant in here so it's not used885            // anywhere besides the function call.886            bool IsInAnonymousNamespace;887            populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},888                                 IsInAnonymousNamespace);889            FI.Access =890                getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());891            BI.Children.Functions.emplace_back(std::move(FI));892          }893        I.Bases.emplace_back(std::move(BI));894        // Call this function recursively to get the inherited classes of895        // this base; these new bases will also get stored in the original896        // RecordInfo: I.897        parseBases(I, Base, IsFileInRootDir, PublicOnly, false,898                   I.Bases.back().Access);899      }900    }901  }902}903 904std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>905emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,906         bool PublicOnly) {907  auto NSI = std::make_unique<NamespaceInfo>();908  bool IsInAnonymousNamespace = false;909  populateInfo(*NSI, D, FC, IsInAnonymousNamespace);910  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))911    return {};912 913  NSI->Name = D->isAnonymousNamespace()914                  ? llvm::SmallString<16>("@nonymous_namespace")915                  : NSI->Name;916  NSI->Path = getInfoRelativePath(NSI->Namespace);917  if (NSI->Namespace.empty() && NSI->USR == SymbolID())918    return {std::unique_ptr<Info>{std::move(NSI)}, nullptr};919 920  // Namespaces are inserted into the parent by reference, so we need to return921  // both the parent and the record itself.922  return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(*NSI)};923}924 925static void parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {926  if (!D->hasDefinition() || !D->hasFriends())927    return;928 929  for (const FriendDecl *FD : D->friends()) {930    if (FD->isUnsupportedFriend())931      continue;932 933    FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));934    const auto *ActualDecl = FD->getFriendDecl();935    if (!ActualDecl) {936      const auto *FriendTypeInfo = FD->getFriendType();937      if (!FriendTypeInfo)938        continue;939      ActualDecl = FriendTypeInfo->getType()->getAsCXXRecordDecl();940 941      if (!ActualDecl)942        continue;943      F.IsClass = true;944    }945 946    if (const auto *ActualTD = dyn_cast_or_null<TemplateDecl>(ActualDecl)) {947      if (isa<RecordDecl>(ActualTD->getTemplatedDecl()))948        F.IsClass = true;949      F.Template.emplace();950      for (const auto *Param : ActualTD->getTemplateParameters()->asArray())951        F.Template->Params.emplace_back(952            getSourceCode(Param, Param->getSourceRange()));953      ActualDecl = ActualTD->getTemplatedDecl();954    }955 956    if (auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(ActualDecl)) {957      FunctionInfo TempInfo;958      parseParameters(TempInfo, FuncDecl);959      F.Params.emplace();960      F.Params = std::move(TempInfo.Params);961      F.ReturnType = getTypeInfoForType(FuncDecl->getReturnType(),962                                        FuncDecl->getLangOpts());963    }964 965    F.Ref =966        Reference(getUSRForDecl(ActualDecl), ActualDecl->getNameAsString(),967                  InfoType::IT_default, ActualDecl->getQualifiedNameAsString(),968                  getInfoRelativePath(ActualDecl));969 970    RI.Friends.push_back(std::move(F));971  }972}973 974std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>975emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,976         bool PublicOnly) {977 978  auto RI = std::make_unique<RecordInfo>();979  bool IsInAnonymousNamespace = false;980 981  populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);982  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))983    return {};984 985  RI->TagType = D->getTagKind();986  parseFields(*RI, D, PublicOnly);987 988  if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {989    if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {990      RI->Name = TD->getNameAsString();991      RI->IsTypeDef = true;992    }993    // TODO: remove first call to parseBases, that function should be deleted994    parseBases(*RI, C);995    parseBases(*RI, C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true);996    parseFriends(*RI, C);997  }998  RI->Path = getInfoRelativePath(RI->Namespace);999 1000  populateTemplateParameters(RI->Template, D);1001  if (RI->Template)1002    populateConstraints(RI->Template.value(), D->getDescribedTemplate());1003 1004  // Full and partial specializations.1005  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {1006    if (!RI->Template)1007      RI->Template.emplace();1008    RI->Template->Specialization.emplace();1009    auto &Specialization = *RI->Template->Specialization;1010 1011    // What this is a specialization of.1012    auto SpecOf = CTSD->getSpecializedTemplateOrPartial();1013    if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(SpecOf))1014      Specialization.SpecializationOf = getUSRForDecl(SpecTD);1015    else if (auto *SpecTD =1016                 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))1017      Specialization.SpecializationOf = getUSRForDecl(SpecTD);1018 1019    // Parameters to the specialization. For partial specializations, get the1020    // parameters "as written" from the ClassTemplatePartialSpecializationDecl1021    // because the non-explicit template parameters will have generated internal1022    // placeholder names rather than the names the user typed that match the1023    // template parameters.1024    if (const ClassTemplatePartialSpecializationDecl *CTPSD =1025            dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {1026      if (const ASTTemplateArgumentListInfo *AsWritten =1027              CTPSD->getTemplateArgsAsWritten()) {1028        for (unsigned Idx = 0; Idx < AsWritten->getNumTemplateArgs(); Idx++) {1029          Specialization.Params.emplace_back(1030              getSourceCode(D, (*AsWritten)[Idx].getSourceRange()));1031        }1032      }1033    } else {1034      for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {1035        Specialization.Params.push_back(convertTemplateArgToInfo(D, Arg));1036      }1037    }1038  }1039 1040  // Records are inserted into the parent by reference, so we need to return1041  // both the parent and the record itself.1042  auto Parent = makeAndInsertIntoParent<const RecordInfo &>(*RI);1043  return {std::move(RI), std::move(Parent)};1044}1045 1046std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>1047emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc,1048         bool PublicOnly) {1049  FunctionInfo Func;1050  bool IsInAnonymousNamespace = false;1051  populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);1052  Func.Access = clang::AccessSpecifier::AS_none;1053  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1054    return {};1055 1056  // Info is wrapped in its parent scope so is returned in the second position.1057  return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};1058}1059 1060std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>1061emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,1062         bool PublicOnly) {1063  FunctionInfo Func;1064  bool IsInAnonymousNamespace = false;1065  populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);1066  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1067    return {};1068 1069  Func.IsMethod = true;1070  Func.IsStatic = D->isStatic();1071 1072  const NamedDecl *Parent = nullptr;1073  if (const auto *SD =1074          dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))1075    Parent = SD->getSpecializedTemplate();1076  else1077    Parent = D->getParent();1078 1079  SymbolID ParentUSR = getUSRForDecl(Parent);1080  Func.Parent =1081      Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,1082                Parent->getQualifiedNameAsString()};1083  Func.Access = D->getAccess();1084 1085  // Info is wrapped in its parent scope so is returned in the second position.1086  return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};1087}1088 1089static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {1090  assert(D && "Invalid Decl when extracting comment");1091  ASTContext &Context = D->getASTContext();1092  RawComment *Comment = Context.getRawCommentForDeclNoCache(D);1093  if (!Comment)1094    return;1095 1096  Comment->setAttached();1097  if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {1098    Info.Description.emplace_back();1099    parseFullComment(Fc, Info.Description.back());1100  }1101}1102 1103std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>1104emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,1105         bool PublicOnly) {1106  TypedefInfo Info;1107  bool IsInAnonymousNamespace = false;1108  populateInfo(Info, D, FC, IsInAnonymousNamespace);1109 1110  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1111    return {};1112 1113  Info.DefLoc = Loc;1114  auto &LO = D->getLangOpts();1115  Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);1116 1117  if (Info.Underlying.Type.Name.empty()) {1118    // Typedef for an unnamed type. This is like "typedef struct { } Foo;"1119    // The record serializer explicitly checks for this syntax and constructs1120    // a record with that name, so we don't want to emit a duplicate here.1121    return {};1122  }1123  Info.IsUsing = false;1124  extractCommentFromDecl(D, Info);1125 1126  // Info is wrapped in its parent scope so is returned in the second position.1127  return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};1128}1129 1130// A type alias is a C++ "using" declaration for a type. It gets mapped to a1131// TypedefInfo with the IsUsing flag set.1132std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>1133emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,1134         bool PublicOnly) {1135  TypedefInfo Info;1136  bool IsInAnonymousNamespace = false;1137  populateInfo(Info, D, FC, IsInAnonymousNamespace);1138  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1139    return {};1140 1141  Info.DefLoc = Loc;1142  const LangOptions &LO = D->getLangOpts();1143  Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);1144  Info.TypeDeclaration = getTypeAlias(D);1145  Info.IsUsing = true;1146 1147  extractCommentFromDecl(D, Info);1148 1149  // Info is wrapped in its parent scope so is returned in the second position.1150  return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};1151}1152 1153std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>1154emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,1155         bool PublicOnly) {1156  EnumInfo Enum;1157  bool IsInAnonymousNamespace = false;1158  populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace);1159 1160  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1161    return {};1162 1163  Enum.Scoped = D->isScoped();1164  if (D->isFixed()) {1165    auto Name = D->getIntegerType().getAsString();1166    Enum.BaseType = TypeInfo(Name, Name);1167  }1168  parseEnumerators(Enum, D);1169 1170  // Info is wrapped in its parent scope so is returned in the second position.1171  return {nullptr, makeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};1172}1173 1174std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>1175emitInfo(const ConceptDecl *D, const FullComment *FC, const Location &Loc,1176         bool PublicOnly) {1177  ConceptInfo Concept;1178 1179  bool IsInAnonymousNamespace = false;1180  populateInfo(Concept, D, FC, IsInAnonymousNamespace);1181  Concept.IsType = D->isTypeConcept();1182  Concept.DefLoc = Loc;1183  Concept.ConstraintExpression = exprToString(D->getConstraintExpr());1184 1185  if (auto *ConceptParams = D->getTemplateParameters()) {1186    for (const auto *Param : ConceptParams->asArray()) {1187      Concept.Template.Params.emplace_back(1188          getSourceCode(Param, Param->getSourceRange()));1189    }1190  }1191 1192  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1193    return {};1194 1195  return {nullptr, makeAndInsertIntoParent<ConceptInfo &&>(std::move(Concept))};1196}1197 1198std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>1199emitInfo(const VarDecl *D, const FullComment *FC, const Location &Loc,1200         bool PublicOnly) {1201  VarInfo Var;1202  bool IsInAnonymousNamespace = false;1203  populateSymbolInfo(Var, D, FC, Loc, IsInAnonymousNamespace);1204  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1205    return {};1206 1207  if (D->getStorageClass() == StorageClass::SC_Static)1208    Var.IsStatic = true;1209  Var.Type =1210      getTypeInfoForType(D->getType(), D->getASTContext().getPrintingPolicy());1211 1212  if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))1213    return {};1214 1215  return {nullptr, makeAndInsertIntoParent<VarInfo &&>(std::move(Var))};1216}1217 1218} // namespace serialize1219} // namespace doc1220} // namespace clang1221