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