clang-tools 20.0.0git
Serialize.cpp
Go to the documentation of this file.
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) {
25 return llvm::SHA1::hash(arrayRefFromStringRef(USR));
26}
27
28template <typename T>
29static void
30populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
31 const 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>
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;
54}
55
56llvm::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);
64}
65
67 : public ConstCommentVisitor<ClangDocCommentVisitor> {
68public:
70
71 void parseComment(const comments::Comment *C);
72
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);
83
84private:
85 std::string getCommandName(unsigned CommandID) const;
86 bool isWhitespaceOnly(StringRef S) const;
87
88 CommentInfo &CurrentCI;
89};
90
91void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
92 CurrentCI.Kind = C->getCommentKindName();
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);
99 }
100}
101
103 if (!isWhitespaceOnly(C->getText()))
104 CurrentCI.Text = C->getText();
105}
106
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));
112}
113
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);
122 }
123}
124
126 const HTMLEndTagComment *C) {
127 CurrentCI.Name = C->getTagName();
128 CurrentCI.SelfClosing = true;
129}
130
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));
136}
137
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();
145}
146
148 const TParamCommandComment *C) {
149 if (C->hasParamName())
150 CurrentCI.ParamName = C->getParamNameAsWritten();
151}
152
154 const VerbatimBlockComment *C) {
155 CurrentCI.Name = getCommandName(C->getCommandID());
156 CurrentCI.CloseName = C->getCloseName();
157}
158
160 const VerbatimBlockLineComment *C) {
161 if (!isWhitespaceOnly(C->getText()))
162 CurrentCI.Text = C->getText();
163}
164
166 const VerbatimLineComment *C) {
167 if (!isWhitespaceOnly(C->getText()))
168 CurrentCI.Text = C->getText();
169}
170
171bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
172 return llvm::all_of(S, isspace);
173}
174
175std::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>";
181}
182
183// Serializing functions.
184
185std::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();
190}
191
192template <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();
198}
199
200std::string serialize(std::unique_ptr<Info> &I) {
201 switch (I->IT) {
203 return serialize(*static_cast<NamespaceInfo *>(I.get()));
205 return serialize(*static_cast<RecordInfo *>(I.get()));
207 return serialize(*static_cast<EnumInfo *>(I.get()));
209 return serialize(*static_cast<FunctionInfo *>(I.get()));
210 default:
211 return "";
212 }
213}
214
215static void parseFullComment(const FullComment *C, CommentInfo &CI) {
216 ClangDocCommentVisitor Visitor(CI);
217 Visitor.parseComment(C);
218}
219
220static SymbolID getUSRForDecl(const Decl *D) {
221 llvm::SmallString<128> USR;
222 if (index::generateUSRForDecl(D, USR))
223 return SymbolID();
224 return hashUSR(USR);
225}
226
227static TagDecl *getTagDeclForType(const QualType &T) {
228 if (const TagDecl *D = T->getAsTagDecl())
229 return D->getDefinition();
230 return nullptr;
231}
232
233static RecordDecl *getRecordDeclForType(const QualType &T) {
234 if (const RecordDecl *D = T->getAsRecordDecl())
235 return D->getDefinition();
236 return nullptr;
237}
238
239TypeInfo getTypeInfoForType(const QualType &T, const PrintingPolicy &Policy) {
240 const TagDecl *TD = getTagDeclForType(T);
241 if (!TD)
242 return TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));
243
244 InfoType IT;
245 if (dyn_cast<EnumDecl>(TD)) {
247 } else if (dyn_cast<RecordDecl>(TD)) {
249 } else {
251 }
252 return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
253 T.getAsString(Policy), getInfoRelativePath(TD)));
254}
255
256static 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
264}
265
266static 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()));
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) {
283 Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
285}
286
287static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
288 Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
290}
291
293 Scope.Enums.push_back(std::move(Info));
294}
295
297 Scope.Functions.push_back(std::move(Info));
298}
299
301 Scope.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) {
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;
325 }
326
327 switch (Child.Namespace[0].RefType) {
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;
333 }
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;
339 }
340 default:
341 llvm_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,
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;
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,
377 AccessSpecifier Access = AccessSpecifier::AS_public) {
378 for (const FieldDecl *F : D->fields()) {
379 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
380 continue;
381
382 auto &LO = F->getLangOpts();
383 // Use getAccessUnsafe so that we just get the default AS_none if it's not
384 // valid, as opposed to an assert.
385 MemberTypeInfo &NewMember = I.Members.emplace_back(
386 getTypeInfoForType(F->getTypeSourceInfo()->getType(), LO),
387 F->getNameAsString(),
388 getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
389 populateMemberTypeInfo(NewMember, F);
390 }
391}
392
393static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
394 for (const EnumConstantDecl *E : D->enumerators()) {
395 std::string ValueExpr;
396 if (const Expr *InitExpr = E->getInitExpr())
397 ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
398 SmallString<16> ValueStr;
399 E->getInitVal().toString(ValueStr);
400 I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
401 ASTContext &Context = E->getASTContext();
402 if (RawComment *Comment =
403 E->getASTContext().getRawCommentForDeclNoCache(E)) {
404 CommentInfo CInfo;
405 Comment->setAttached();
406 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
407 EnumValueInfo &Member = I.Members.back();
408 Member.Description.emplace_back();
409 parseFullComment(Fc, Member.Description.back());
410 }
411 }
412 }
413}
414
415static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
416 auto &LO = D->getLangOpts();
417 for (const ParmVarDecl *P : D->parameters()) {
418 FieldTypeInfo &FieldInfo = I.Params.emplace_back(
419 getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());
420 FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
421 }
422}
423
424// TODO: Remove the serialization of Parents and VirtualParents, this
425// information is also extracted in the other definition of parseBases.
426static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
427 // Don't parse bases if this isn't a definition.
428 if (!D->isThisDeclarationADefinition())
429 return;
430 for (const CXXBaseSpecifier &B : D->bases()) {
431 if (B.isVirtual())
432 continue;
433 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
434 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
435 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
436 InfoType::IT_record, B.getType().getAsString());
437 } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
438 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
439 InfoType::IT_record, P->getQualifiedNameAsString(),
441 else
442 I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
443 }
444 for (const CXXBaseSpecifier &B : D->vbases()) {
445 if (const RecordDecl *P = getRecordDeclForType(B.getType()))
446 I.VirtualParents.emplace_back(
447 getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
448 P->getQualifiedNameAsString(), getInfoRelativePath(P));
449 else
450 I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
451 }
452}
453
454template <typename T>
455static void
456populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
457 const T *D, bool &IsInAnonymousNamespace) {
458 const DeclContext *DC = D->getDeclContext();
459 do {
460 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
461 std::string Namespace;
462 if (N->isAnonymousNamespace()) {
463 Namespace = "@nonymous_namespace";
464 IsInAnonymousNamespace = true;
465 } else
466 Namespace = N->getNameAsString();
467 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
469 N->getQualifiedNameAsString());
470 } else if (const auto *N = dyn_cast<RecordDecl>(DC))
471 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
473 N->getQualifiedNameAsString());
474 else if (const auto *N = dyn_cast<FunctionDecl>(DC))
475 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
477 N->getQualifiedNameAsString());
478 else if (const auto *N = dyn_cast<EnumDecl>(DC))
479 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
480 InfoType::IT_enum, N->getQualifiedNameAsString());
481 } while ((DC = DC->getParent()));
482 // The global namespace should be added to the list of namespaces if the decl
483 // corresponds to a Record and if it doesn't have any namespace (because this
484 // means it's in the global namespace). Also if its outermost namespace is a
485 // record because that record matches the previous condition mentioned.
486 if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
487 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
488 Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
490}
491
492void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
493 const clang::Decl *D) {
494 if (const TemplateParameterList *ParamList =
495 D->getDescribedTemplateParams()) {
496 if (!TemplateInfo) {
497 TemplateInfo.emplace();
498 }
499 for (const NamedDecl *ND : *ParamList) {
500 TemplateInfo->Params.emplace_back(
501 getSourceCode(ND, ND->getSourceRange()));
502 }
503 }
504}
505
507 const TemplateArgument &Arg) {
508 // The TemplateArgument's pretty printing handles all the normal cases
509 // well enough for our requirements.
510 std::string Str;
511 llvm::raw_string_ostream Stream(Str);
512 Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
513 return TemplateParamInfo(Str);
514}
515
516template <typename T>
517static void populateInfo(Info &I, const T *D, const FullComment *C,
518 bool &IsInAnonymousNamespace) {
519 I.USR = getUSRForDecl(D);
520 I.Name = D->getNameAsString();
521 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
522 if (C) {
523 I.Description.emplace_back();
524 parseFullComment(C, I.Description.back());
525 }
526}
527
528template <typename T>
529static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
530 int LineNumber, StringRef Filename,
531 bool IsFileInRootDir,
532 bool &IsInAnonymousNamespace) {
533 populateInfo(I, D, C, IsInAnonymousNamespace);
534 if (D->isThisDeclarationADefinition())
535 I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
536 else
537 I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
538}
539
540static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
541 const FullComment *FC, int LineNumber,
542 StringRef Filename, bool IsFileInRootDir,
543 bool &IsInAnonymousNamespace) {
544 populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
545 IsInAnonymousNamespace);
546 auto &LO = D->getLangOpts();
547 I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
548 parseParameters(I, D);
549
551
552 // Handle function template specializations.
553 if (const FunctionTemplateSpecializationInfo *FTSI =
554 D->getTemplateSpecializationInfo()) {
555 if (!I.Template)
556 I.Template.emplace();
557 I.Template->Specialization.emplace();
558 auto &Specialization = *I.Template->Specialization;
559
560 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
561
562 // Template parameters to the specialization.
563 if (FTSI->TemplateArguments) {
564 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
565 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
566 }
567 }
568 }
569}
570
571static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
572 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
573
574 ASTContext& Context = D->getASTContext();
575 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
576 // of this logic. See also similar code in Mapper.cpp.
577 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
578 if (!Comment)
579 return;
580
581 Comment->setAttached();
582 if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
583 I.Description.emplace_back();
584 parseFullComment(fc, I.Description.back());
585 }
586}
587
588static void
589parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
590 bool PublicOnly, bool IsParent,
591 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
592 // Don't parse bases if this isn't a definition.
593 if (!D->isThisDeclarationADefinition())
594 return;
595 for (const CXXBaseSpecifier &B : D->bases()) {
596 if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
597 if (const CXXRecordDecl *Base =
598 cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
599 // Initialized without USR and name, this will be set in the following
600 // if-else stmt.
602 {}, "", getInfoRelativePath(Base), B.isVirtual(),
603 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
604 IsParent);
605 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
606 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
607 BI.USR = getUSRForDecl(D);
608 BI.Name = B.getType().getAsString();
609 } else {
610 BI.USR = getUSRForDecl(Base);
611 BI.Name = Base->getNameAsString();
612 }
613 parseFields(BI, Base, PublicOnly, BI.Access);
614 for (const auto &Decl : Base->decls())
615 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
616 // Don't serialize private methods
617 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
618 !MD->isUserProvided())
619 continue;
620 FunctionInfo FI;
621 FI.IsMethod = true;
622 // The seventh arg in populateFunctionInfo is a boolean passed by
623 // reference, its value is not relevant in here so it's not used
624 // anywhere besides the function call.
625 bool IsInAnonymousNamespace;
626 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
627 /*FileName=*/{}, IsFileInRootDir,
628 IsInAnonymousNamespace);
629 FI.Access =
630 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
631 BI.Children.Functions.emplace_back(std::move(FI));
632 }
633 I.Bases.emplace_back(std::move(BI));
634 // Call this function recursively to get the inherited classes of
635 // this base; these new bases will also get stored in the original
636 // RecordInfo: I.
637 parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
638 I.Bases.back().Access);
639 }
640 }
641 }
642}
643
644std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
645emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
646 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
647 auto I = std::make_unique<NamespaceInfo>();
648 bool IsInAnonymousNamespace = false;
649 populateInfo(*I, D, FC, IsInAnonymousNamespace);
650 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
651 return {};
652
653 I->Name = D->isAnonymousNamespace()
654 ? llvm::SmallString<16>("@nonymous_namespace")
655 : I->Name;
656 I->Path = getInfoRelativePath(I->Namespace);
657 if (I->Namespace.empty() && I->USR == SymbolID())
658 return {std::unique_ptr<Info>{std::move(I)}, nullptr};
659
660 // Namespaces are inserted into the parent by reference, so we need to return
661 // both the parent and the record itself.
662 return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
663}
664
665std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
666emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
667 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
668 auto I = std::make_unique<RecordInfo>();
669 bool IsInAnonymousNamespace = false;
670 populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
671 IsInAnonymousNamespace);
672 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
673 return {};
674
675 I->TagType = D->getTagKind();
676 parseFields(*I, D, PublicOnly);
677 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
678 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
679 I->Name = TD->getNameAsString();
680 I->IsTypeDef = true;
681 }
682 // TODO: remove first call to parseBases, that function should be deleted
683 parseBases(*I, C);
684 parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
685 }
686 I->Path = getInfoRelativePath(I->Namespace);
687
688 PopulateTemplateParameters(I->Template, D);
689
690 // Full and partial specializations.
691 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
692 if (!I->Template)
693 I->Template.emplace();
694 I->Template->Specialization.emplace();
695 auto &Specialization = *I->Template->Specialization;
696
697 // What this is a specialization of.
698 auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
699 if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
700 Specialization.SpecializationOf = getUSRForDecl(CTD);
701 else if (auto *CTPSD =
702 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
703 Specialization.SpecializationOf = getUSRForDecl(CTPSD);
704
705 // Parameters to the specilization. For partial specializations, get the
706 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
707 // because the non-explicit template parameters will have generated internal
708 // placeholder names rather than the names the user typed that match the
709 // template parameters.
710 if (const ClassTemplatePartialSpecializationDecl *CTPSD =
711 dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
712 if (const ASTTemplateArgumentListInfo *AsWritten =
713 CTPSD->getTemplateArgsAsWritten()) {
714 for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
715 Specialization.Params.emplace_back(
716 getSourceCode(D, (*AsWritten)[i].getSourceRange()));
717 }
718 }
719 } else {
720 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
721 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
722 }
723 }
724 }
725
726 // Records are inserted into the parent by reference, so we need to return
727 // both the parent and the record itself.
728 auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
729 return {std::move(I), std::move(Parent)};
730}
731
732std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
733emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
734 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
735 FunctionInfo Func;
736 bool IsInAnonymousNamespace = false;
737 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
738 IsInAnonymousNamespace);
739 Func.Access = clang::AccessSpecifier::AS_none;
740 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
741 return {};
742
743 // Info is wrapped in its parent scope so is returned in the second position.
744 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
745}
746
747std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
748emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
749 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
750 FunctionInfo Func;
751 bool IsInAnonymousNamespace = false;
752 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
753 IsInAnonymousNamespace);
754 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
755 return {};
756
757 Func.IsMethod = true;
758
759 const NamedDecl *Parent = nullptr;
760 if (const auto *SD =
761 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
762 Parent = SD->getSpecializedTemplate();
763 else
764 Parent = D->getParent();
765
766 SymbolID ParentUSR = getUSRForDecl(Parent);
767 Func.Parent =
768 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
769 Parent->getQualifiedNameAsString()};
770 Func.Access = D->getAccess();
771
772 // Info is wrapped in its parent scope so is returned in the second position.
773 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
774}
775
776std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
777emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
778 StringRef File, bool IsFileInRootDir, bool PublicOnly) {
780
781 bool IsInAnonymousNamespace = false;
782 populateInfo(Info, D, FC, IsInAnonymousNamespace);
783 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
784 return {};
785
786 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
787 auto &LO = D->getLangOpts();
788 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
789 if (Info.Underlying.Type.Name.empty()) {
790 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
791 // The record serializer explicitly checks for this syntax and constructs
792 // a record with that name, so we don't want to emit a duplicate here.
793 return {};
794 }
795 Info.IsUsing = false;
796
797 // Info is wrapped in its parent scope so is returned in the second position.
798 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
799}
800
801// A type alias is a C++ "using" declaration for a type. It gets mapped to a
802// TypedefInfo with the IsUsing flag set.
803std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
804emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
805 StringRef File, bool IsFileInRootDir, bool PublicOnly) {
807
808 bool IsInAnonymousNamespace = false;
809 populateInfo(Info, D, FC, IsInAnonymousNamespace);
810 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
811 return {};
812
813 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
814 auto &LO = D->getLangOpts();
815 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
816 Info.IsUsing = true;
817
818 // Info is wrapped in its parent scope so is returned in the second position.
819 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
820}
821
822std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
823emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
824 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
825 EnumInfo Enum;
826 bool IsInAnonymousNamespace = false;
827 populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
828 IsInAnonymousNamespace);
829 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
830 return {};
831
832 Enum.Scoped = D->isScoped();
833 if (D->isFixed()) {
834 auto Name = D->getIntegerType().getAsString();
835 Enum.BaseType = TypeInfo(Name, Name);
836 }
837 parseEnumerators(Enum, D);
838
839 // Info is wrapped in its parent scope so is returned in the second position.
840 return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
841}
842
843} // namespace serialize
844} // namespace doc
845} // namespace clang
const Expr * E
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
static llvm::cl::opt< bool > PublicOnly("public", llvm::cl::desc("Document only public declarations."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory))
const Node * Parent
const Criteria C
std::string Filename
Filename as a string.
std::vector< HeaderHandle > Path
std::string USR
std::unique_ptr< CompilerInvocation > CI
void emitBlock(const NamespaceInfo &I)
void visitHTMLEndTagComment(const HTMLEndTagComment *C)
Definition: Serialize.cpp:125
void visitVerbatimLineComment(const VerbatimLineComment *C)
Definition: Serialize.cpp:165
void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C)
Definition: Serialize.cpp:159
void visitHTMLStartTagComment(const HTMLStartTagComment *C)
Definition: Serialize.cpp:114
void visitTParamCommandComment(const TParamCommandComment *C)
Definition: Serialize.cpp:147
void visitVerbatimBlockComment(const VerbatimBlockComment *C)
Definition: Serialize.cpp:153
void visitParamCommandComment(const ParamCommandComment *C)
Definition: Serialize.cpp:138
void visitInlineCommandComment(const InlineCommandComment *C)
Definition: Serialize.cpp:107
void parseComment(const comments::Comment *C)
Definition: Serialize.cpp:91
void visitTextComment(const TextComment *C)
Definition: Serialize.cpp:102
void visitBlockCommandComment(const BlockCommandComment *C)
Definition: Serialize.cpp:131
static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, AccessSpecifier Access=AccessSpecifier::AS_public)
Definition: Serialize.cpp:376
static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D)
Definition: Serialize.cpp:571
static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace, const NamedDecl *D)
Definition: Serialize.cpp:266
static RecordDecl * getRecordDeclForType(const QualType &T)
Definition: Serialize.cpp:233
static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, const FullComment *FC, int LineNumber, StringRef Filename, bool IsFileInRootDir, bool &IsInAnonymousNamespace)
Definition: Serialize.cpp:540
void PopulateTemplateParameters(std::optional< TemplateInfo > &TemplateInfo, const clang::Decl *D)
Definition: Serialize.cpp:492
std::string getSourceCode(const Decl *D, const SourceRange &R)
Definition: Serialize.cpp:185
std::pair< std::unique_ptr< Info >, std::unique_ptr< Info > > emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber, llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly)
Definition: Serialize.cpp:645
static bool isPublic(const clang::AccessSpecifier AS, const clang::Linkage Link)
Definition: Serialize.cpp:256
llvm::SmallString< 128 > getInfoRelativePath(const llvm::SmallVectorImpl< doc::Reference > &Namespaces)
Definition: Serialize.cpp:49
static void parseFullComment(const FullComment *C, CommentInfo &CI)
Definition: Serialize.cpp:215
std::unique_ptr< Info > MakeAndInsertIntoParent(ChildType Child)
Definition: Serialize.cpp:319
static std::string serialize(T &I)
Definition: Serialize.cpp:192
static void parseParameters(FunctionInfo &I, const FunctionDecl *D)
Definition: Serialize.cpp:415
static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, int LineNumber, StringRef Filename, bool IsFileInRootDir, bool &IsInAnonymousNamespace)
Definition: Serialize.cpp:529
static SymbolID getUSRForDecl(const Decl *D)
Definition: Serialize.cpp:220
TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D, const TemplateArgument &Arg)
Definition: Serialize.cpp:506
static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS, AccessSpecifier SecondAS)
Definition: Serialize.cpp:360
static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info)
Definition: Serialize.cpp:282
TypeInfo getTypeInfoForType(const QualType &T, const PrintingPolicy &Policy)
Definition: Serialize.cpp:239
static void populateParentNamespaces(llvm::SmallVector< Reference, 4 > &Namespaces, const T *D, bool &IsAnonymousNamespace)
Definition: Serialize.cpp:456
static void parseEnumerators(EnumInfo &I, const EnumDecl *D)
Definition: Serialize.cpp:393
static TagDecl * getTagDeclForType(const QualType &T)
Definition: Serialize.cpp:227
static void parseBases(RecordInfo &I, const CXXRecordDecl *D)
Definition: Serialize.cpp:426
SymbolID hashUSR(llvm::StringRef USR)
Definition: Serialize.cpp:24
static void populateInfo(Info &I, const T *D, const FullComment *C, bool &IsInAnonymousNamespace)
Definition: Serialize.cpp:517
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
std::array< uint8_t, 20 > SymbolID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
SmallString< 8 > Direction
SmallString< 16 > Kind
std::vector< std::unique_ptr< CommentInfo > > Children
llvm::SmallVector< SmallString< 16 >, 4 > AttrValues
SmallString< 16 > CloseName
SmallString< 16 > Name
SmallString< 64 > Text
llvm::SmallVector< SmallString< 16 >, 4 > AttrKeys
llvm::SmallVector< SmallString< 16 >, 4 > Args
SmallString< 16 > ParamName
llvm::SmallVector< EnumValueInfo, 4 > Members
std::vector< CommentInfo > Description
SmallString< 16 > DefaultValue
llvm::SmallVector< FieldTypeInfo, 4 > Params
std::optional< TemplateInfo > Template
A base struct for Infos.
SmallString< 16 > Name
std::vector< CommentInfo > Description
llvm::SmallVector< Reference, 4 > Namespace
std::vector< CommentInfo > Description
llvm::SmallVector< MemberTypeInfo, 4 > Members
llvm::SmallVector< Reference, 4 > VirtualParents
llvm::SmallVector< Reference, 4 > Parents
std::vector< BaseRecordInfo > Bases
std::vector< Reference > Records
std::vector< TypedefInfo > Typedefs
std::vector< FunctionInfo > Functions
std::vector< Reference > Namespaces
std::vector< EnumInfo > Enums
llvm::SmallVector< Location, 2 > Loc
std::optional< Location > DefLoc
std::vector< TemplateParamInfo > Params