llvm-project
832 строки · 31.7 Кб
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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Serialize.h"10#include "BitcodeWriter.h"11#include "clang/AST/Comment.h"12#include "clang/Index/USRGeneration.h"13#include "clang/Lex/Lexer.h"14#include "llvm/ADT/Hashing.h"15#include "llvm/ADT/StringExtras.h"16#include "llvm/Support/SHA1.h"17
18using clang::comments::FullComment;19
20namespace clang {21namespace doc {22namespace serialize {23
24SymbolID hashUSR(llvm::StringRef USR) {25return llvm::SHA1::hash(arrayRefFromStringRef(USR));26}
27
28template <typename T>29static void30populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,31const T *D, bool &IsAnonymousNamespace);32
33static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);34
35// A function to extract the appropriate relative path for a given info's
36// documentation. The path returned is a composite of the parent namespaces.
37//
38// Example: Given the below, the directory path for class C info will be
39// <root>/A/B
40//
41// namespace A {
42// namespace B {
43//
44// class C {};
45//
46// }
47// }
48llvm::SmallString<128>49getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {50llvm::SmallString<128> Path;51for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)52llvm::sys::path::append(Path, R->Name);53return Path;54}
55
56llvm::SmallString<128> getInfoRelativePath(const Decl *D) {57llvm::SmallVector<Reference, 4> Namespaces;58// The third arg in populateParentNamespaces is a boolean passed by reference,59// its value is not relevant in here so it's not used anywhere besides the60// function call61bool B = true;62populateParentNamespaces(Namespaces, D, B);63return getInfoRelativePath(Namespaces);64}
65
66class ClangDocCommentVisitor67: public ConstCommentVisitor<ClangDocCommentVisitor> {68public:69ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}70
71void parseComment(const comments::Comment *C);72
73void visitTextComment(const TextComment *C);74void visitInlineCommandComment(const InlineCommandComment *C);75void visitHTMLStartTagComment(const HTMLStartTagComment *C);76void visitHTMLEndTagComment(const HTMLEndTagComment *C);77void visitBlockCommandComment(const BlockCommandComment *C);78void visitParamCommandComment(const ParamCommandComment *C);79void visitTParamCommandComment(const TParamCommandComment *C);80void visitVerbatimBlockComment(const VerbatimBlockComment *C);81void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);82void visitVerbatimLineComment(const VerbatimLineComment *C);83
84private:85std::string getCommandName(unsigned CommandID) const;86bool isWhitespaceOnly(StringRef S) const;87
88CommentInfo &CurrentCI;89};90
91void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {92CurrentCI.Kind = C->getCommentKindName();93ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);94for (comments::Comment *Child :95llvm::make_range(C->child_begin(), C->child_end())) {96CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());97ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());98Visitor.parseComment(Child);99}100}
101
102void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {103if (!isWhitespaceOnly(C->getText()))104CurrentCI.Text = C->getText();105}
106
107void ClangDocCommentVisitor::visitInlineCommandComment(108const InlineCommandComment *C) {109CurrentCI.Name = getCommandName(C->getCommandID());110for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)111CurrentCI.Args.push_back(C->getArgText(I));112}
113
114void ClangDocCommentVisitor::visitHTMLStartTagComment(115const HTMLStartTagComment *C) {116CurrentCI.Name = C->getTagName();117CurrentCI.SelfClosing = C->isSelfClosing();118for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {119const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);120CurrentCI.AttrKeys.push_back(Attr.Name);121CurrentCI.AttrValues.push_back(Attr.Value);122}123}
124
125void ClangDocCommentVisitor::visitHTMLEndTagComment(126const HTMLEndTagComment *C) {127CurrentCI.Name = C->getTagName();128CurrentCI.SelfClosing = true;129}
130
131void ClangDocCommentVisitor::visitBlockCommandComment(132const BlockCommandComment *C) {133CurrentCI.Name = getCommandName(C->getCommandID());134for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)135CurrentCI.Args.push_back(C->getArgText(I));136}
137
138void ClangDocCommentVisitor::visitParamCommandComment(139const ParamCommandComment *C) {140CurrentCI.Direction =141ParamCommandComment::getDirectionAsString(C->getDirection());142CurrentCI.Explicit = C->isDirectionExplicit();143if (C->hasParamName())144CurrentCI.ParamName = C->getParamNameAsWritten();145}
146
147void ClangDocCommentVisitor::visitTParamCommandComment(148const TParamCommandComment *C) {149if (C->hasParamName())150CurrentCI.ParamName = C->getParamNameAsWritten();151}
152
153void ClangDocCommentVisitor::visitVerbatimBlockComment(154const VerbatimBlockComment *C) {155CurrentCI.Name = getCommandName(C->getCommandID());156CurrentCI.CloseName = C->getCloseName();157}
158
159void ClangDocCommentVisitor::visitVerbatimBlockLineComment(160const VerbatimBlockLineComment *C) {161if (!isWhitespaceOnly(C->getText()))162CurrentCI.Text = C->getText();163}
164
165void ClangDocCommentVisitor::visitVerbatimLineComment(166const VerbatimLineComment *C) {167if (!isWhitespaceOnly(C->getText()))168CurrentCI.Text = C->getText();169}
170
171bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {172return llvm::all_of(S, isspace);173}
174
175std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {176const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);177if (Info)178return Info->Name;179// TODO: Add parsing for \file command.180return "<not a builtin command>";181}
182
183// Serializing functions.
184
185std::string getSourceCode(const Decl *D, const SourceRange &R) {186return Lexer::getSourceText(CharSourceRange::getTokenRange(R),187D->getASTContext().getSourceManager(),188D->getASTContext().getLangOpts())189.str();190}
191
192template <typename T> static std::string serialize(T &I) {193SmallString<2048> Buffer;194llvm::BitstreamWriter Stream(Buffer);195ClangDocBitcodeWriter Writer(Stream);196Writer.emitBlock(I);197return Buffer.str().str();198}
199
200std::string serialize(std::unique_ptr<Info> &I) {201switch (I->IT) {202case InfoType::IT_namespace:203return serialize(*static_cast<NamespaceInfo *>(I.get()));204case InfoType::IT_record:205return serialize(*static_cast<RecordInfo *>(I.get()));206case InfoType::IT_enum:207return serialize(*static_cast<EnumInfo *>(I.get()));208case InfoType::IT_function:209return serialize(*static_cast<FunctionInfo *>(I.get()));210default:211return "";212}213}
214
215static void parseFullComment(const FullComment *C, CommentInfo &CI) {216ClangDocCommentVisitor Visitor(CI);217Visitor.parseComment(C);218}
219
220static SymbolID getUSRForDecl(const Decl *D) {221llvm::SmallString<128> USR;222if (index::generateUSRForDecl(D, USR))223return SymbolID();224return hashUSR(USR);225}
226
227static TagDecl *getTagDeclForType(const QualType &T) {228if (const TagDecl *D = T->getAsTagDecl())229return D->getDefinition();230return nullptr;231}
232
233static RecordDecl *getRecordDeclForType(const QualType &T) {234if (const RecordDecl *D = T->getAsRecordDecl())235return D->getDefinition();236return nullptr;237}
238
239TypeInfo getTypeInfoForType(const QualType &T) {240const TagDecl *TD = getTagDeclForType(T);241if (!TD)242return TypeInfo(Reference(SymbolID(), T.getAsString()));243
244InfoType IT;245if (dyn_cast<EnumDecl>(TD)) {246IT = InfoType::IT_enum;247} else if (dyn_cast<RecordDecl>(TD)) {248IT = InfoType::IT_record;249} else {250IT = InfoType::IT_default;251}252return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,253T.getAsString(), getInfoRelativePath(TD)));254}
255
256static bool isPublic(const clang::AccessSpecifier AS,257const clang::Linkage Link) {258if (AS == clang::AccessSpecifier::AS_private)259return false;260else if ((Link == clang::Linkage::Module) ||261(Link == clang::Linkage::External))262return true;263return false; // otherwise, linkage is some form of internal linkage264}
265
266static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,267const NamedDecl *D) {268bool IsAnonymousNamespace = false;269if (const auto *N = dyn_cast<NamespaceDecl>(D))270IsAnonymousNamespace = N->isAnonymousNamespace();271return !PublicOnly ||272(!IsInAnonymousNamespace && !IsAnonymousNamespace &&273isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));274}
275
276// The InsertChild functions insert the given info into the given scope using
277// the method appropriate for that type. Some types are moved into the
278// appropriate vector, while other types have Reference objects generated to
279// refer to them.
280//
281// See MakeAndInsertIntoParent().
282static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {283Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,284Info.Name, getInfoRelativePath(Info.Namespace));285}
286
287static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {288Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,289Info.Name, getInfoRelativePath(Info.Namespace));290}
291
292static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {293Scope.Enums.push_back(std::move(Info));294}
295
296static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {297Scope.Functions.push_back(std::move(Info));298}
299
300static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {301Scope.Typedefs.push_back(std::move(Info));302}
303
304// Creates a parent of the correct type for the given child and inserts it into
305// that parent.
306//
307// This is complicated by the fact that namespaces and records are inserted by
308// reference (constructing a "Reference" object with that namespace/record's
309// info), while everything else is inserted by moving it directly into the child
310// vectors.
311//
312// For namespaces and records, explicitly specify a const& template parameter
313// when invoking this function:
314// MakeAndInsertIntoParent<const Record&>(...);
315// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
316// parameter. Since each variant is used once, it's not worth having a more
317// elaborate system to automatically deduce this information.
318template <typename ChildType>319std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {320if (Child.Namespace.empty()) {321// Insert into unnamed parent namespace.322auto ParentNS = std::make_unique<NamespaceInfo>();323InsertChild(ParentNS->Children, std::forward<ChildType>(Child));324return ParentNS;325}326
327switch (Child.Namespace[0].RefType) {328case InfoType::IT_namespace: {329auto ParentNS = std::make_unique<NamespaceInfo>();330ParentNS->USR = Child.Namespace[0].USR;331InsertChild(ParentNS->Children, std::forward<ChildType>(Child));332return ParentNS;333}334case InfoType::IT_record: {335auto ParentRec = std::make_unique<RecordInfo>();336ParentRec->USR = Child.Namespace[0].USR;337InsertChild(ParentRec->Children, std::forward<ChildType>(Child));338return ParentRec;339}340default:341llvm_unreachable("Invalid reference type for parent namespace");342}343}
344
345// There are two uses for this function.
346// 1) Getting the resulting mode of inheritance of a record.
347// Example: class A {}; class B : private A {}; class C : public B {};
348// It's explicit that C is publicly inherited from C and B is privately
349// inherited from A. It's not explicit but C is also privately inherited from
350// A. This is the AS that this function calculates. FirstAS is the
351// inheritance mode of `class C : B` and SecondAS is the inheritance mode of
352// `class B : A`.
353// 2) Getting the inheritance mode of an inherited attribute / method.
354// Example : class A { public: int M; }; class B : private A {};
355// Class B is inherited from class A, which has a public attribute. This
356// attribute is now part of the derived class B but it's not public. This
357// will be private because the inheritance is private. This is the AS that
358// this function calculates. FirstAS is the inheritance mode and SecondAS is
359// the AS of the attribute / method.
360static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,361AccessSpecifier SecondAS) {362if (FirstAS == AccessSpecifier::AS_none ||363SecondAS == AccessSpecifier::AS_none)364return AccessSpecifier::AS_none;365if (FirstAS == AccessSpecifier::AS_private ||366SecondAS == AccessSpecifier::AS_private)367return AccessSpecifier::AS_private;368if (FirstAS == AccessSpecifier::AS_protected ||369SecondAS == AccessSpecifier::AS_protected)370return AccessSpecifier::AS_protected;371return AccessSpecifier::AS_public;372}
373
374// The Access parameter is only provided when parsing the field of an inherited
375// record, the access specification of the field depends on the inheritance mode
376static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,377AccessSpecifier Access = AccessSpecifier::AS_public) {378for (const FieldDecl *F : D->fields()) {379if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))380continue;381
382// Use getAccessUnsafe so that we just get the default AS_none if it's not383// valid, as opposed to an assert.384MemberTypeInfo &NewMember = I.Members.emplace_back(385getTypeInfoForType(F->getTypeSourceInfo()->getType()),386F->getNameAsString(),387getFinalAccessSpecifier(Access, F->getAccessUnsafe()));388populateMemberTypeInfo(NewMember, F);389}390}
391
392static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {393for (const EnumConstantDecl *E : D->enumerators()) {394std::string ValueExpr;395if (const Expr *InitExpr = E->getInitExpr())396ValueExpr = getSourceCode(D, InitExpr->getSourceRange());397
398SmallString<16> ValueStr;399E->getInitVal().toString(ValueStr);400I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);401}402}
403
404static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {405for (const ParmVarDecl *P : D->parameters()) {406FieldTypeInfo &FieldInfo = I.Params.emplace_back(407getTypeInfoForType(P->getOriginalType()), P->getNameAsString());408FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());409}410}
411
412// TODO: Remove the serialization of Parents and VirtualParents, this
413// information is also extracted in the other definition of parseBases.
414static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {415// Don't parse bases if this isn't a definition.416if (!D->isThisDeclarationADefinition())417return;418for (const CXXBaseSpecifier &B : D->bases()) {419if (B.isVirtual())420continue;421if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {422const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();423I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),424InfoType::IT_record, B.getType().getAsString());425} else if (const RecordDecl *P = getRecordDeclForType(B.getType()))426I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),427InfoType::IT_record, P->getQualifiedNameAsString(),428getInfoRelativePath(P));429else430I.Parents.emplace_back(SymbolID(), B.getType().getAsString());431}432for (const CXXBaseSpecifier &B : D->vbases()) {433if (const RecordDecl *P = getRecordDeclForType(B.getType()))434I.VirtualParents.emplace_back(435getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,436P->getQualifiedNameAsString(), getInfoRelativePath(P));437else438I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());439}440}
441
442template <typename T>443static void444populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,445const T *D, bool &IsInAnonymousNamespace) {446const DeclContext *DC = D->getDeclContext();447do {448if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {449std::string Namespace;450if (N->isAnonymousNamespace()) {451Namespace = "@nonymous_namespace";452IsInAnonymousNamespace = true;453} else454Namespace = N->getNameAsString();455Namespaces.emplace_back(getUSRForDecl(N), Namespace,456InfoType::IT_namespace,457N->getQualifiedNameAsString());458} else if (const auto *N = dyn_cast<RecordDecl>(DC))459Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),460InfoType::IT_record,461N->getQualifiedNameAsString());462else if (const auto *N = dyn_cast<FunctionDecl>(DC))463Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),464InfoType::IT_function,465N->getQualifiedNameAsString());466else if (const auto *N = dyn_cast<EnumDecl>(DC))467Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),468InfoType::IT_enum, N->getQualifiedNameAsString());469} while ((DC = DC->getParent()));470// The global namespace should be added to the list of namespaces if the decl471// corresponds to a Record and if it doesn't have any namespace (because this472// means it's in the global namespace). Also if its outermost namespace is a473// record because that record matches the previous condition mentioned.474if ((Namespaces.empty() && isa<RecordDecl>(D)) ||475(!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))476Namespaces.emplace_back(SymbolID(), "GlobalNamespace",477InfoType::IT_namespace);478}
479
480void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,481const clang::Decl *D) {482if (const TemplateParameterList *ParamList =483D->getDescribedTemplateParams()) {484if (!TemplateInfo) {485TemplateInfo.emplace();486}487for (const NamedDecl *ND : *ParamList) {488TemplateInfo->Params.emplace_back(489getSourceCode(ND, ND->getSourceRange()));490}491}492}
493
494TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,495const TemplateArgument &Arg) {496// The TemplateArgument's pretty printing handles all the normal cases497// well enough for our requirements.498std::string Str;499llvm::raw_string_ostream Stream(Str);500Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);501return TemplateParamInfo(Str);502}
503
504template <typename T>505static void populateInfo(Info &I, const T *D, const FullComment *C,506bool &IsInAnonymousNamespace) {507I.USR = getUSRForDecl(D);508I.Name = D->getNameAsString();509populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);510if (C) {511I.Description.emplace_back();512parseFullComment(C, I.Description.back());513}514}
515
516template <typename T>517static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,518int LineNumber, StringRef Filename,519bool IsFileInRootDir,520bool &IsInAnonymousNamespace) {521populateInfo(I, D, C, IsInAnonymousNamespace);522if (D->isThisDeclarationADefinition())523I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);524else525I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);526}
527
528static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,529const FullComment *FC, int LineNumber,530StringRef Filename, bool IsFileInRootDir,531bool &IsInAnonymousNamespace) {532populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,533IsInAnonymousNamespace);534I.ReturnType = getTypeInfoForType(D->getReturnType());535parseParameters(I, D);536
537PopulateTemplateParameters(I.Template, D);538
539// Handle function template specializations.540if (const FunctionTemplateSpecializationInfo *FTSI =541D->getTemplateSpecializationInfo()) {542if (!I.Template)543I.Template.emplace();544I.Template->Specialization.emplace();545auto &Specialization = *I.Template->Specialization;546
547Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());548
549// Template parameters to the specialization.550if (FTSI->TemplateArguments) {551for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {552Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));553}554}555}556}
557
558static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {559assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");560
561ASTContext& Context = D->getASTContext();562// TODO investigate whether we can use ASTContext::getCommentForDecl instead563// of this logic. See also similar code in Mapper.cpp.564RawComment *Comment = Context.getRawCommentForDeclNoCache(D);565if (!Comment)566return;567
568Comment->setAttached();569if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {570I.Description.emplace_back();571parseFullComment(fc, I.Description.back());572}573}
574
575static void576parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,577bool PublicOnly, bool IsParent,578AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {579// Don't parse bases if this isn't a definition.580if (!D->isThisDeclarationADefinition())581return;582for (const CXXBaseSpecifier &B : D->bases()) {583if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {584if (const CXXRecordDecl *Base =585cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {586// Initialized without USR and name, this will be set in the following587// if-else stmt.588BaseRecordInfo BI(589{}, "", getInfoRelativePath(Base), B.isVirtual(),590getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),591IsParent);592if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {593const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();594BI.USR = getUSRForDecl(D);595BI.Name = B.getType().getAsString();596} else {597BI.USR = getUSRForDecl(Base);598BI.Name = Base->getNameAsString();599}600parseFields(BI, Base, PublicOnly, BI.Access);601for (const auto &Decl : Base->decls())602if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {603// Don't serialize private methods604if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||605!MD->isUserProvided())606continue;607FunctionInfo FI;608FI.IsMethod = true;609// The seventh arg in populateFunctionInfo is a boolean passed by610// reference, its value is not relevant in here so it's not used611// anywhere besides the function call.612bool IsInAnonymousNamespace;613populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},614/*FileName=*/{}, IsFileInRootDir,615IsInAnonymousNamespace);616FI.Access =617getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());618BI.Children.Functions.emplace_back(std::move(FI));619}620I.Bases.emplace_back(std::move(BI));621// Call this function recursively to get the inherited classes of622// this base; these new bases will also get stored in the original623// RecordInfo: I.624parseBases(I, Base, IsFileInRootDir, PublicOnly, false,625I.Bases.back().Access);626}627}628}629}
630
631std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>632emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,633llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {634auto I = std::make_unique<NamespaceInfo>();635bool IsInAnonymousNamespace = false;636populateInfo(*I, D, FC, IsInAnonymousNamespace);637if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))638return {};639
640I->Name = D->isAnonymousNamespace()641? llvm::SmallString<16>("@nonymous_namespace")642: I->Name;643I->Path = getInfoRelativePath(I->Namespace);644if (I->Namespace.empty() && I->USR == SymbolID())645return {std::unique_ptr<Info>{std::move(I)}, nullptr};646
647// Namespaces are inserted into the parent by reference, so we need to return648// both the parent and the record itself.649return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};650}
651
652std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>653emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,654llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {655auto I = std::make_unique<RecordInfo>();656bool IsInAnonymousNamespace = false;657populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,658IsInAnonymousNamespace);659if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))660return {};661
662I->TagType = D->getTagKind();663parseFields(*I, D, PublicOnly);664if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {665if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {666I->Name = TD->getNameAsString();667I->IsTypeDef = true;668}669// TODO: remove first call to parseBases, that function should be deleted670parseBases(*I, C);671parseBases(*I, C, IsFileInRootDir, PublicOnly, true);672}673I->Path = getInfoRelativePath(I->Namespace);674
675PopulateTemplateParameters(I->Template, D);676
677// Full and partial specializations.678if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {679if (!I->Template)680I->Template.emplace();681I->Template->Specialization.emplace();682auto &Specialization = *I->Template->Specialization;683
684// What this is a specialization of.685auto SpecOf = CTSD->getSpecializedTemplateOrPartial();686if (SpecOf.is<ClassTemplateDecl *>()) {687Specialization.SpecializationOf =688getUSRForDecl(SpecOf.get<ClassTemplateDecl *>());689} else if (SpecOf.is<ClassTemplatePartialSpecializationDecl *>()) {690Specialization.SpecializationOf =691getUSRForDecl(SpecOf.get<ClassTemplatePartialSpecializationDecl *>());692}693
694// Parameters to the specilization. For partial specializations, get the695// parameters "as written" from the ClassTemplatePartialSpecializationDecl696// because the non-explicit template parameters will have generated internal697// placeholder names rather than the names the user typed that match the698// template parameters.699if (const ClassTemplatePartialSpecializationDecl *CTPSD =700dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {701if (const ASTTemplateArgumentListInfo *AsWritten =702CTPSD->getTemplateArgsAsWritten()) {703for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {704Specialization.Params.emplace_back(705getSourceCode(D, (*AsWritten)[i].getSourceRange()));706}707}708} else {709for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {710Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));711}712}713}714
715// Records are inserted into the parent by reference, so we need to return716// both the parent and the record itself.717auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);718return {std::move(I), std::move(Parent)};719}
720
721std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>722emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,723llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {724FunctionInfo Func;725bool IsInAnonymousNamespace = false;726populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,727IsInAnonymousNamespace);728Func.Access = clang::AccessSpecifier::AS_none;729if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))730return {};731
732// Info is wrapped in its parent scope so is returned in the second position.733return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};734}
735
736std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>737emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,738llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {739FunctionInfo Func;740bool IsInAnonymousNamespace = false;741populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,742IsInAnonymousNamespace);743if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))744return {};745
746Func.IsMethod = true;747
748const NamedDecl *Parent = nullptr;749if (const auto *SD =750dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))751Parent = SD->getSpecializedTemplate();752else753Parent = D->getParent();754
755SymbolID ParentUSR = getUSRForDecl(Parent);756Func.Parent =757Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,758Parent->getQualifiedNameAsString()};759Func.Access = D->getAccess();760
761// Info is wrapped in its parent scope so is returned in the second position.762return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};763}
764
765std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>766emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,767StringRef File, bool IsFileInRootDir, bool PublicOnly) {768TypedefInfo Info;769
770bool IsInAnonymousNamespace = false;771populateInfo(Info, D, FC, IsInAnonymousNamespace);772if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))773return {};774
775Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);776Info.Underlying = getTypeInfoForType(D->getUnderlyingType());777if (Info.Underlying.Type.Name.empty()) {778// Typedef for an unnamed type. This is like "typedef struct { } Foo;"779// The record serializer explicitly checks for this syntax and constructs780// a record with that name, so we don't want to emit a duplicate here.781return {};782}783Info.IsUsing = false;784
785// Info is wrapped in its parent scope so is returned in the second position.786return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};787}
788
789// A type alias is a C++ "using" declaration for a type. It gets mapped to a
790// TypedefInfo with the IsUsing flag set.
791std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>792emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,793StringRef File, bool IsFileInRootDir, bool PublicOnly) {794TypedefInfo Info;795
796bool IsInAnonymousNamespace = false;797populateInfo(Info, D, FC, IsInAnonymousNamespace);798if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))799return {};800
801Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);802Info.Underlying = getTypeInfoForType(D->getUnderlyingType());803Info.IsUsing = true;804
805// Info is wrapped in its parent scope so is returned in the second position.806return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};807}
808
809std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>810emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,811llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {812EnumInfo Enum;813bool IsInAnonymousNamespace = false;814populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,815IsInAnonymousNamespace);816if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))817return {};818
819Enum.Scoped = D->isScoped();820if (D->isFixed()) {821auto Name = D->getIntegerType().getAsString();822Enum.BaseType = TypeInfo(Name, Name);823}824parseEnumerators(Enum, D);825
826// Info is wrapped in its parent scope so is returned in the second position.827return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};828}
829
830} // namespace serialize831} // namespace doc832} // namespace clang833