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

source code of clang-tools-extra/clang-doc/Serialize.cpp