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