[ELF] Refactor merge-* tests
[llvm-project.git] / clang-tools-extra / clang-doc / Serialize.cpp
blobb9db78cf7d688fb72a0cb6fcec033f67beb5e062
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 //===----------------------------------------------------------------------===//
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"
18 using clang::comments::FullComment;
20 namespace clang {
21 namespace doc {
22 namespace serialize {
24 SymbolID hashUSR(llvm::StringRef USR) {
25 return llvm::SHA1::hash(arrayRefFromStringRef(USR));
28 template <typename T>
29 static void
30 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
31 const T *D, bool &IsAnonymousNamespace);
33 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
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.
38 // Example: Given the below, the directory path for class C info will be
39 // <root>/A/B
41 // namespace A {
42 // namespace B {
44 // class C {};
46 // }
47 // }
48 llvm::SmallString<128>
49 getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
50 llvm::SmallString<128> Path;
51 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
52 llvm::sys::path::append(Path, R->Name);
53 return Path;
56 llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
57 llvm::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 the
60 // function call
61 bool B = true;
62 populateParentNamespaces(Namespaces, D, B);
63 return getInfoRelativePath(Namespaces);
66 class ClangDocCommentVisitor
67 : public ConstCommentVisitor<ClangDocCommentVisitor> {
68 public:
69 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
71 void parseComment(const comments::Comment *C);
73 void visitTextComment(const TextComment *C);
74 void visitInlineCommandComment(const InlineCommandComment *C);
75 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
76 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
77 void visitBlockCommandComment(const BlockCommandComment *C);
78 void visitParamCommandComment(const ParamCommandComment *C);
79 void visitTParamCommandComment(const TParamCommandComment *C);
80 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
81 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
82 void visitVerbatimLineComment(const VerbatimLineComment *C);
84 private:
85 std::string getCommandName(unsigned CommandID) const;
86 bool isWhitespaceOnly(StringRef S) const;
88 CommentInfo &CurrentCI;
91 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
92 CurrentCI.Kind = C->getCommentKindName();
93 ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
94 for (comments::Comment *Child :
95 llvm::make_range(C->child_begin(), C->child_end())) {
96 CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
97 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
98 Visitor.parseComment(Child);
102 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
103 if (!isWhitespaceOnly(C->getText()))
104 CurrentCI.Text = C->getText();
107 void ClangDocCommentVisitor::visitInlineCommandComment(
108 const InlineCommandComment *C) {
109 CurrentCI.Name = getCommandName(C->getCommandID());
110 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
111 CurrentCI.Args.push_back(C->getArgText(I));
114 void ClangDocCommentVisitor::visitHTMLStartTagComment(
115 const HTMLStartTagComment *C) {
116 CurrentCI.Name = C->getTagName();
117 CurrentCI.SelfClosing = C->isSelfClosing();
118 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
119 const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
120 CurrentCI.AttrKeys.push_back(Attr.Name);
121 CurrentCI.AttrValues.push_back(Attr.Value);
125 void ClangDocCommentVisitor::visitHTMLEndTagComment(
126 const HTMLEndTagComment *C) {
127 CurrentCI.Name = C->getTagName();
128 CurrentCI.SelfClosing = true;
131 void ClangDocCommentVisitor::visitBlockCommandComment(
132 const BlockCommandComment *C) {
133 CurrentCI.Name = getCommandName(C->getCommandID());
134 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
135 CurrentCI.Args.push_back(C->getArgText(I));
138 void ClangDocCommentVisitor::visitParamCommandComment(
139 const ParamCommandComment *C) {
140 CurrentCI.Direction =
141 ParamCommandComment::getDirectionAsString(C->getDirection());
142 CurrentCI.Explicit = C->isDirectionExplicit();
143 if (C->hasParamName())
144 CurrentCI.ParamName = C->getParamNameAsWritten();
147 void ClangDocCommentVisitor::visitTParamCommandComment(
148 const TParamCommandComment *C) {
149 if (C->hasParamName())
150 CurrentCI.ParamName = C->getParamNameAsWritten();
153 void ClangDocCommentVisitor::visitVerbatimBlockComment(
154 const VerbatimBlockComment *C) {
155 CurrentCI.Name = getCommandName(C->getCommandID());
156 CurrentCI.CloseName = C->getCloseName();
159 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
160 const VerbatimBlockLineComment *C) {
161 if (!isWhitespaceOnly(C->getText()))
162 CurrentCI.Text = C->getText();
165 void ClangDocCommentVisitor::visitVerbatimLineComment(
166 const VerbatimLineComment *C) {
167 if (!isWhitespaceOnly(C->getText()))
168 CurrentCI.Text = C->getText();
171 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
172 return llvm::all_of(S, isspace);
175 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
176 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
177 if (Info)
178 return Info->Name;
179 // TODO: Add parsing for \file command.
180 return "<not a builtin command>";
183 // Serializing functions.
185 std::string getSourceCode(const Decl *D, const SourceRange &R) {
186 return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
187 D->getASTContext().getSourceManager(),
188 D->getASTContext().getLangOpts())
189 .str();
192 template <typename T> static std::string serialize(T &I) {
193 SmallString<2048> Buffer;
194 llvm::BitstreamWriter Stream(Buffer);
195 ClangDocBitcodeWriter Writer(Stream);
196 Writer.emitBlock(I);
197 return Buffer.str().str();
200 std::string serialize(std::unique_ptr<Info> &I) {
201 switch (I->IT) {
202 case InfoType::IT_namespace:
203 return serialize(*static_cast<NamespaceInfo *>(I.get()));
204 case InfoType::IT_record:
205 return serialize(*static_cast<RecordInfo *>(I.get()));
206 case InfoType::IT_enum:
207 return serialize(*static_cast<EnumInfo *>(I.get()));
208 case InfoType::IT_function:
209 return serialize(*static_cast<FunctionInfo *>(I.get()));
210 default:
211 return "";
215 static void parseFullComment(const FullComment *C, CommentInfo &CI) {
216 ClangDocCommentVisitor Visitor(CI);
217 Visitor.parseComment(C);
220 static SymbolID getUSRForDecl(const Decl *D) {
221 llvm::SmallString<128> USR;
222 if (index::generateUSRForDecl(D, USR))
223 return SymbolID();
224 return hashUSR(USR);
227 static TagDecl *getTagDeclForType(const QualType &T) {
228 if (const TagDecl *D = T->getAsTagDecl())
229 return D->getDefinition();
230 return nullptr;
233 static RecordDecl *getRecordDeclForType(const QualType &T) {
234 if (const RecordDecl *D = T->getAsRecordDecl())
235 return D->getDefinition();
236 return nullptr;
239 TypeInfo getTypeInfoForType(const QualType &T) {
240 const TagDecl *TD = getTagDeclForType(T);
241 if (!TD)
242 return TypeInfo(Reference(SymbolID(), T.getAsString()));
244 InfoType IT;
245 if (dyn_cast<EnumDecl>(TD)) {
246 IT = InfoType::IT_enum;
247 } else if (dyn_cast<RecordDecl>(TD)) {
248 IT = InfoType::IT_record;
249 } else {
250 IT = InfoType::IT_default;
252 return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
253 T.getAsString(), getInfoRelativePath(TD)));
256 static bool isPublic(const clang::AccessSpecifier AS,
257 const clang::Linkage Link) {
258 if (AS == clang::AccessSpecifier::AS_private)
259 return false;
260 else if ((Link == clang::Linkage::Module) ||
261 (Link == clang::Linkage::External))
262 return true;
263 return false; // otherwise, linkage is some form of internal linkage
266 static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
267 const NamedDecl *D) {
268 bool IsAnonymousNamespace = false;
269 if (const auto *N = dyn_cast<NamespaceDecl>(D))
270 IsAnonymousNamespace = N->isAnonymousNamespace();
271 return !PublicOnly ||
272 (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
273 isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
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.
281 // See MakeAndInsertIntoParent().
282 static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
283 Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
284 Info.Name, getInfoRelativePath(Info.Namespace));
287 static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
288 Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
289 Info.Name, getInfoRelativePath(Info.Namespace));
292 static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
293 Scope.Enums.push_back(std::move(Info));
296 static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
297 Scope.Functions.push_back(std::move(Info));
300 static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
301 Scope.Typedefs.push_back(std::move(Info));
304 // Creates a parent of the correct type for the given child and inserts it into
305 // that parent.
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.
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.
318 template <typename ChildType>
319 std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
320 if (Child.Namespace.empty()) {
321 // Insert into unnamed parent namespace.
322 auto ParentNS = std::make_unique<NamespaceInfo>();
323 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
324 return ParentNS;
327 switch (Child.Namespace[0].RefType) {
328 case InfoType::IT_namespace: {
329 auto ParentNS = std::make_unique<NamespaceInfo>();
330 ParentNS->USR = Child.Namespace[0].USR;
331 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
332 return ParentNS;
334 case InfoType::IT_record: {
335 auto ParentRec = std::make_unique<RecordInfo>();
336 ParentRec->USR = Child.Namespace[0].USR;
337 InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
338 return ParentRec;
340 default:
341 llvm_unreachable("Invalid reference type for parent namespace");
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.
360 static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
361 AccessSpecifier SecondAS) {
362 if (FirstAS == AccessSpecifier::AS_none ||
363 SecondAS == AccessSpecifier::AS_none)
364 return AccessSpecifier::AS_none;
365 if (FirstAS == AccessSpecifier::AS_private ||
366 SecondAS == AccessSpecifier::AS_private)
367 return AccessSpecifier::AS_private;
368 if (FirstAS == AccessSpecifier::AS_protected ||
369 SecondAS == AccessSpecifier::AS_protected)
370 return AccessSpecifier::AS_protected;
371 return AccessSpecifier::AS_public;
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
376 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
377 AccessSpecifier Access = AccessSpecifier::AS_public) {
378 for (const FieldDecl *F : D->fields()) {
379 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
380 continue;
382 // Use getAccessUnsafe so that we just get the default AS_none if it's not
383 // valid, as opposed to an assert.
384 MemberTypeInfo &NewMember = I.Members.emplace_back(
385 getTypeInfoForType(F->getTypeSourceInfo()->getType()),
386 F->getNameAsString(),
387 getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
388 populateMemberTypeInfo(NewMember, F);
392 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
393 for (const EnumConstantDecl *E : D->enumerators()) {
394 std::string ValueExpr;
395 if (const Expr *InitExpr = E->getInitExpr())
396 ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
397 SmallString<16> ValueStr;
398 E->getInitVal().toString(ValueStr);
399 I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
400 ASTContext &Context = E->getASTContext();
401 if (RawComment *Comment =
402 E->getASTContext().getRawCommentForDeclNoCache(E)) {
403 CommentInfo CInfo;
404 Comment->setAttached();
405 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
406 EnumValueInfo &Member = I.Members.back();
407 Member.Description.emplace_back();
408 parseFullComment(Fc, Member.Description.back());
414 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
415 for (const ParmVarDecl *P : D->parameters()) {
416 FieldTypeInfo &FieldInfo = I.Params.emplace_back(
417 getTypeInfoForType(P->getOriginalType()), P->getNameAsString());
418 FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
422 // TODO: Remove the serialization of Parents and VirtualParents, this
423 // information is also extracted in the other definition of parseBases.
424 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
425 // Don't parse bases if this isn't a definition.
426 if (!D->isThisDeclarationADefinition())
427 return;
428 for (const CXXBaseSpecifier &B : D->bases()) {
429 if (B.isVirtual())
430 continue;
431 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
432 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
433 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
434 InfoType::IT_record, B.getType().getAsString());
435 } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
436 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
437 InfoType::IT_record, P->getQualifiedNameAsString(),
438 getInfoRelativePath(P));
439 else
440 I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
442 for (const CXXBaseSpecifier &B : D->vbases()) {
443 if (const RecordDecl *P = getRecordDeclForType(B.getType()))
444 I.VirtualParents.emplace_back(
445 getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
446 P->getQualifiedNameAsString(), getInfoRelativePath(P));
447 else
448 I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
452 template <typename T>
453 static void
454 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
455 const T *D, bool &IsInAnonymousNamespace) {
456 const DeclContext *DC = D->getDeclContext();
457 do {
458 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
459 std::string Namespace;
460 if (N->isAnonymousNamespace()) {
461 Namespace = "@nonymous_namespace";
462 IsInAnonymousNamespace = true;
463 } else
464 Namespace = N->getNameAsString();
465 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
466 InfoType::IT_namespace,
467 N->getQualifiedNameAsString());
468 } else if (const auto *N = dyn_cast<RecordDecl>(DC))
469 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
470 InfoType::IT_record,
471 N->getQualifiedNameAsString());
472 else if (const auto *N = dyn_cast<FunctionDecl>(DC))
473 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
474 InfoType::IT_function,
475 N->getQualifiedNameAsString());
476 else if (const auto *N = dyn_cast<EnumDecl>(DC))
477 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
478 InfoType::IT_enum, N->getQualifiedNameAsString());
479 } while ((DC = DC->getParent()));
480 // The global namespace should be added to the list of namespaces if the decl
481 // corresponds to a Record and if it doesn't have any namespace (because this
482 // means it's in the global namespace). Also if its outermost namespace is a
483 // record because that record matches the previous condition mentioned.
484 if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
485 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
486 Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
487 InfoType::IT_namespace);
490 void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
491 const clang::Decl *D) {
492 if (const TemplateParameterList *ParamList =
493 D->getDescribedTemplateParams()) {
494 if (!TemplateInfo) {
495 TemplateInfo.emplace();
497 for (const NamedDecl *ND : *ParamList) {
498 TemplateInfo->Params.emplace_back(
499 getSourceCode(ND, ND->getSourceRange()));
504 TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
505 const TemplateArgument &Arg) {
506 // The TemplateArgument's pretty printing handles all the normal cases
507 // well enough for our requirements.
508 std::string Str;
509 llvm::raw_string_ostream Stream(Str);
510 Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
511 return TemplateParamInfo(Str);
514 template <typename T>
515 static void populateInfo(Info &I, const T *D, const FullComment *C,
516 bool &IsInAnonymousNamespace) {
517 I.USR = getUSRForDecl(D);
518 I.Name = D->getNameAsString();
519 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
520 if (C) {
521 I.Description.emplace_back();
522 parseFullComment(C, I.Description.back());
526 template <typename T>
527 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
528 int LineNumber, StringRef Filename,
529 bool IsFileInRootDir,
530 bool &IsInAnonymousNamespace) {
531 populateInfo(I, D, C, IsInAnonymousNamespace);
532 if (D->isThisDeclarationADefinition())
533 I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
534 else
535 I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
538 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
539 const FullComment *FC, int LineNumber,
540 StringRef Filename, bool IsFileInRootDir,
541 bool &IsInAnonymousNamespace) {
542 populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
543 IsInAnonymousNamespace);
544 I.ReturnType = getTypeInfoForType(D->getReturnType());
545 parseParameters(I, D);
547 PopulateTemplateParameters(I.Template, D);
549 // Handle function template specializations.
550 if (const FunctionTemplateSpecializationInfo *FTSI =
551 D->getTemplateSpecializationInfo()) {
552 if (!I.Template)
553 I.Template.emplace();
554 I.Template->Specialization.emplace();
555 auto &Specialization = *I.Template->Specialization;
557 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
559 // Template parameters to the specialization.
560 if (FTSI->TemplateArguments) {
561 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
562 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
568 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
569 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
571 ASTContext& Context = D->getASTContext();
572 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
573 // of this logic. See also similar code in Mapper.cpp.
574 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
575 if (!Comment)
576 return;
578 Comment->setAttached();
579 if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
580 I.Description.emplace_back();
581 parseFullComment(fc, I.Description.back());
585 static void
586 parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
587 bool PublicOnly, bool IsParent,
588 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
589 // Don't parse bases if this isn't a definition.
590 if (!D->isThisDeclarationADefinition())
591 return;
592 for (const CXXBaseSpecifier &B : D->bases()) {
593 if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
594 if (const CXXRecordDecl *Base =
595 cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
596 // Initialized without USR and name, this will be set in the following
597 // if-else stmt.
598 BaseRecordInfo BI(
599 {}, "", getInfoRelativePath(Base), B.isVirtual(),
600 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
601 IsParent);
602 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
603 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
604 BI.USR = getUSRForDecl(D);
605 BI.Name = B.getType().getAsString();
606 } else {
607 BI.USR = getUSRForDecl(Base);
608 BI.Name = Base->getNameAsString();
610 parseFields(BI, Base, PublicOnly, BI.Access);
611 for (const auto &Decl : Base->decls())
612 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
613 // Don't serialize private methods
614 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
615 !MD->isUserProvided())
616 continue;
617 FunctionInfo FI;
618 FI.IsMethod = true;
619 // The seventh arg in populateFunctionInfo is a boolean passed by
620 // reference, its value is not relevant in here so it's not used
621 // anywhere besides the function call.
622 bool IsInAnonymousNamespace;
623 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
624 /*FileName=*/{}, IsFileInRootDir,
625 IsInAnonymousNamespace);
626 FI.Access =
627 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
628 BI.Children.Functions.emplace_back(std::move(FI));
630 I.Bases.emplace_back(std::move(BI));
631 // Call this function recursively to get the inherited classes of
632 // this base; these new bases will also get stored in the original
633 // RecordInfo: I.
634 parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
635 I.Bases.back().Access);
641 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
642 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
643 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
644 auto I = std::make_unique<NamespaceInfo>();
645 bool IsInAnonymousNamespace = false;
646 populateInfo(*I, D, FC, IsInAnonymousNamespace);
647 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
648 return {};
650 I->Name = D->isAnonymousNamespace()
651 ? llvm::SmallString<16>("@nonymous_namespace")
652 : I->Name;
653 I->Path = getInfoRelativePath(I->Namespace);
654 if (I->Namespace.empty() && I->USR == SymbolID())
655 return {std::unique_ptr<Info>{std::move(I)}, nullptr};
657 // Namespaces are inserted into the parent by reference, so we need to return
658 // both the parent and the record itself.
659 return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
662 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
663 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
664 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
665 auto I = std::make_unique<RecordInfo>();
666 bool IsInAnonymousNamespace = false;
667 populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
668 IsInAnonymousNamespace);
669 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
670 return {};
672 I->TagType = D->getTagKind();
673 parseFields(*I, D, PublicOnly);
674 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
675 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
676 I->Name = TD->getNameAsString();
677 I->IsTypeDef = true;
679 // TODO: remove first call to parseBases, that function should be deleted
680 parseBases(*I, C);
681 parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
683 I->Path = getInfoRelativePath(I->Namespace);
685 PopulateTemplateParameters(I->Template, D);
687 // Full and partial specializations.
688 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
689 if (!I->Template)
690 I->Template.emplace();
691 I->Template->Specialization.emplace();
692 auto &Specialization = *I->Template->Specialization;
694 // What this is a specialization of.
695 auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
696 if (SpecOf.is<ClassTemplateDecl *>()) {
697 Specialization.SpecializationOf =
698 getUSRForDecl(SpecOf.get<ClassTemplateDecl *>());
699 } else if (SpecOf.is<ClassTemplatePartialSpecializationDecl *>()) {
700 Specialization.SpecializationOf =
701 getUSRForDecl(SpecOf.get<ClassTemplatePartialSpecializationDecl *>());
704 // Parameters to the specilization. For partial specializations, get the
705 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
706 // because the non-explicit template parameters will have generated internal
707 // placeholder names rather than the names the user typed that match the
708 // template parameters.
709 if (const ClassTemplatePartialSpecializationDecl *CTPSD =
710 dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
711 if (const ASTTemplateArgumentListInfo *AsWritten =
712 CTPSD->getTemplateArgsAsWritten()) {
713 for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
714 Specialization.Params.emplace_back(
715 getSourceCode(D, (*AsWritten)[i].getSourceRange()));
718 } else {
719 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
720 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
725 // Records are inserted into the parent by reference, so we need to return
726 // both the parent and the record itself.
727 auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
728 return {std::move(I), std::move(Parent)};
731 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
732 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
733 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
734 FunctionInfo Func;
735 bool IsInAnonymousNamespace = false;
736 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
737 IsInAnonymousNamespace);
738 Func.Access = clang::AccessSpecifier::AS_none;
739 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
740 return {};
742 // Info is wrapped in its parent scope so is returned in the second position.
743 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
746 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
747 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
748 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
749 FunctionInfo Func;
750 bool IsInAnonymousNamespace = false;
751 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
752 IsInAnonymousNamespace);
753 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
754 return {};
756 Func.IsMethod = true;
758 const NamedDecl *Parent = nullptr;
759 if (const auto *SD =
760 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
761 Parent = SD->getSpecializedTemplate();
762 else
763 Parent = D->getParent();
765 SymbolID ParentUSR = getUSRForDecl(Parent);
766 Func.Parent =
767 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
768 Parent->getQualifiedNameAsString()};
769 Func.Access = D->getAccess();
771 // Info is wrapped in its parent scope so is returned in the second position.
772 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
775 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
776 emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
777 StringRef File, bool IsFileInRootDir, bool PublicOnly) {
778 TypedefInfo Info;
780 bool IsInAnonymousNamespace = false;
781 populateInfo(Info, D, FC, IsInAnonymousNamespace);
782 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
783 return {};
785 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
786 Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
787 if (Info.Underlying.Type.Name.empty()) {
788 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
789 // The record serializer explicitly checks for this syntax and constructs
790 // a record with that name, so we don't want to emit a duplicate here.
791 return {};
793 Info.IsUsing = false;
795 // Info is wrapped in its parent scope so is returned in the second position.
796 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
799 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
800 // TypedefInfo with the IsUsing flag set.
801 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
802 emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
803 StringRef File, bool IsFileInRootDir, bool PublicOnly) {
804 TypedefInfo Info;
806 bool IsInAnonymousNamespace = false;
807 populateInfo(Info, D, FC, IsInAnonymousNamespace);
808 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
809 return {};
811 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
812 Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
813 Info.IsUsing = true;
815 // Info is wrapped in its parent scope so is returned in the second position.
816 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
819 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
820 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
821 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
822 EnumInfo Enum;
823 bool IsInAnonymousNamespace = false;
824 populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
825 IsInAnonymousNamespace);
826 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
827 return {};
829 Enum.Scoped = D->isScoped();
830 if (D->isFixed()) {
831 auto Name = D->getIntegerType().getAsString();
832 Enum.BaseType = TypeInfo(Name, Name);
834 parseEnumerators(Enum, D);
836 // Info is wrapped in its parent scope so is returned in the second position.
837 return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
840 } // namespace serialize
841 } // namespace doc
842 } // namespace clang