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// 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(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(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>
245 llvm::SmallString<128> Path;
246 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
247 llvm::sys::path::append(Path, 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, B);
258 return getInfoRelativePath(Namespaces);
259}
260
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(C->getCommentKindName());
289 for (comments::Comment *Child :
290 llvm::make_range(C->child_begin(), C->child_end())) {
291 CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
292 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
293 Visitor.parseComment(Child);
294 }
295}
296
298 if (!isWhitespaceOnly(C->getText()))
299 CurrentCI.Text = C->getText();
300}
301
303 const InlineCommandComment *C) {
304 CurrentCI.Name = getCommandName(C->getCommandID());
305 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
306 CurrentCI.Args.push_back(C->getArgText(I));
307}
308
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(I);
315 CurrentCI.AttrKeys.push_back(Attr.Name);
316 CurrentCI.AttrValues.push_back(Attr.Value);
317 }
318}
319
321 const HTMLEndTagComment *C) {
322 CurrentCI.Name = C->getTagName();
323 CurrentCI.SelfClosing = true;
324}
325
327 const BlockCommandComment *C) {
328 CurrentCI.Name = getCommandName(C->getCommandID());
329 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
330 CurrentCI.Args.push_back(C->getArgText(I));
331}
332
334 const ParamCommandComment *C) {
335 CurrentCI.Direction =
336 ParamCommandComment::getDirectionAsString(C->getDirection());
337 CurrentCI.Explicit = C->isDirectionExplicit();
338 if (C->hasParamName())
339 CurrentCI.ParamName = C->getParamNameAsWritten();
340}
341
343 const TParamCommandComment *C) {
344 if (C->hasParamName())
345 CurrentCI.ParamName = C->getParamNameAsWritten();
346}
347
349 const VerbatimBlockComment *C) {
350 CurrentCI.Name = getCommandName(C->getCommandID());
351 CurrentCI.CloseName = C->getCloseName();
352}
353
355 const VerbatimBlockLineComment *C) {
356 if (!isWhitespaceOnly(C->getText()))
357 CurrentCI.Text = C->getText();
358}
359
361 const VerbatimLineComment *C) {
362 if (!isWhitespaceOnly(C->getText()))
363 CurrentCI.Text = C->getText();
364}
365
366bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
367 return llvm::all_of(S, 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(CharSourceRange::getTokenRange(R),
382 D->getASTContext().getSourceManager(),
383 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) {
398 return serialize(*static_cast<NamespaceInfo *>(I.get()));
400 return serialize(*static_cast<RecordInfo *>(I.get()));
402 return serialize(*static_cast<EnumInfo *>(I.get()));
404 return serialize(*static_cast<FunctionInfo *>(I.get()));
406 return serialize(*static_cast<ConceptInfo *>(I.get()));
408 return serialize(*static_cast<VarInfo *>(I.get()));
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, 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>(TD)) {
453 } else if (isa<RecordDecl>(TD)) {
455 } else {
457 }
458 Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
459 T.getAsString(Policy), getInfoRelativePath(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>(D))
479 IsAnonymousNamespace = N->isAnonymousNamespace();
480 return !PublicOnly ||
481 (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
482 isPublic(D->getAccessUnsafe(), 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().
495
496static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
497 Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
499 Info.MangledName);
500}
501
503 Scope.Enums.push_back(std::move(Info));
504}
505
507 Scope.Functions.push_back(std::move(Info));
508}
509
511 Scope.Typedefs.push_back(std::move(Info));
512}
513
515 Scope.Concepts.push_back(std::move(Info));
516}
517
518static void InsertChild(ScopeChildren &Scope, VarInfo Info) {
519 Scope.Variables.push_back(std::move(Info));
520}
521
522// Creates a parent of the correct type for the given child and inserts it into
523// that parent.
524//
525// This is complicated by the fact that namespaces and records are inserted by
526// reference (constructing a "Reference" object with that namespace/record's
527// info), while everything else is inserted by moving it directly into the child
528// vectors.
529//
530// For namespaces and records, explicitly specify a const& template parameter
531// when invoking this function:
532// MakeAndInsertIntoParent<const Record&>(...);
533// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
534// parameter. Since each variant is used once, it's not worth having a more
535// elaborate system to automatically deduce this information.
536template <typename ChildType>
537static std::unique_ptr<Info> makeAndInsertIntoParent(ChildType Child) {
538 if (Child.Namespace.empty()) {
539 // Insert into unnamed parent namespace.
540 auto ParentNS = std::make_unique<NamespaceInfo>();
541 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
542 return ParentNS;
543 }
544
545 switch (Child.Namespace[0].RefType) {
547 auto ParentNS = std::make_unique<NamespaceInfo>();
548 ParentNS->USR = Child.Namespace[0].USR;
549 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
550 return ParentNS;
551 }
552 case InfoType::IT_record: {
553 auto ParentRec = std::make_unique<RecordInfo>();
554 ParentRec->USR = Child.Namespace[0].USR;
555 InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
556 return ParentRec;
557 }
565 break;
566 }
567 llvm_unreachable("Invalid reference type for parent namespace");
568}
569
570// There are two uses for this function.
571// 1) Getting the resulting mode of inheritance of a record.
572// Example: class A {}; class B : private A {}; class C : public B {};
573// It's explicit that C is publicly inherited from C and B is privately
574// inherited from A. It's not explicit but C is also privately inherited from
575// A. This is the AS that this function calculates. FirstAS is the
576// inheritance mode of `class C : B` and SecondAS is the inheritance mode of
577// `class B : A`.
578// 2) Getting the inheritance mode of an inherited attribute / method.
579// Example : class A { public: int M; }; class B : private A {};
580// Class B is inherited from class A, which has a public attribute. This
581// attribute is now part of the derived class B but it's not public. This
582// will be private because the inheritance is private. This is the AS that
583// this function calculates. FirstAS is the inheritance mode and SecondAS is
584// the AS of the attribute / method.
585static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
586 AccessSpecifier SecondAS) {
587 if (FirstAS == AccessSpecifier::AS_none ||
588 SecondAS == AccessSpecifier::AS_none)
589 return AccessSpecifier::AS_none;
590 if (FirstAS == AccessSpecifier::AS_private ||
591 SecondAS == AccessSpecifier::AS_private)
592 return AccessSpecifier::AS_private;
593 if (FirstAS == AccessSpecifier::AS_protected ||
594 SecondAS == AccessSpecifier::AS_protected)
595 return AccessSpecifier::AS_protected;
596 return AccessSpecifier::AS_public;
597}
598
599// The Access parameter is only provided when parsing the field of an inherited
600// record, the access specification of the field depends on the inheritance mode
601static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
602 AccessSpecifier Access = AccessSpecifier::AS_public) {
603 for (const FieldDecl *F : D->fields()) {
604 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
605 continue;
606 populateMemberTypeInfo(I, Access, F);
607 }
608 const auto *CxxRD = dyn_cast<CXXRecordDecl>(D);
609 if (!CxxRD)
610 return;
611 for (Decl *CxxDecl : CxxRD->decls()) {
612 auto *VD = dyn_cast<VarDecl>(CxxDecl);
613 if (!VD ||
614 !shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, VD))
615 continue;
616
617 if (VD->isStaticDataMember())
618 populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true);
619 }
620}
621
622static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
623 for (const EnumConstantDecl *E : D->enumerators()) {
624 std::string ValueExpr;
625 if (const Expr *InitExpr = E->getInitExpr())
626 ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
627 SmallString<16> ValueStr;
628 E->getInitVal().toString(ValueStr);
629 I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
630 ASTContext &Context = E->getASTContext();
631 if (RawComment *Comment =
632 E->getASTContext().getRawCommentForDeclNoCache(E)) {
633 Comment->setAttached();
634 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
635 EnumValueInfo &Member = I.Members.back();
636 Member.Description.emplace_back();
637 parseFullComment(Fc, Member.Description.back());
638 }
639 }
640 }
641}
642
643static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
644 auto &LO = D->getLangOpts();
645 for (const ParmVarDecl *P : D->parameters()) {
646 FieldTypeInfo &FieldInfo = I.Params.emplace_back(
647 getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());
648 FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
649 }
650}
651
652// TODO: Remove the serialization of Parents and VirtualParents, this
653// information is also extracted in the other definition of parseBases.
654static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
655 // Don't parse bases if this isn't a definition.
656 if (!D->isThisDeclarationADefinition())
657 return;
658
659 for (const CXXBaseSpecifier &B : D->bases()) {
660 if (B.isVirtual())
661 continue;
662 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
663 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
664 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
665 InfoType::IT_record, B.getType().getAsString());
666 } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
667 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
668 InfoType::IT_record, P->getQualifiedNameAsString(),
670 else
671 I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
672 }
673 for (const CXXBaseSpecifier &B : D->vbases()) {
674 if (const RecordDecl *P = getRecordDeclForType(B.getType()))
675 I.VirtualParents.emplace_back(
676 getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
677 P->getQualifiedNameAsString(), getInfoRelativePath(P));
678 else
679 I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
680 }
681}
682
683template <typename T>
684static void
685populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
686 const T *D, bool &IsInAnonymousNamespace) {
687 const DeclContext *DC = D->getDeclContext();
688 do {
689 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
690 std::string Namespace;
691 if (N->isAnonymousNamespace()) {
692 Namespace = "@nonymous_namespace";
693 IsInAnonymousNamespace = true;
694 } else
695 Namespace = N->getNameAsString();
696 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
698 N->getQualifiedNameAsString());
699 } else if (const auto *N = dyn_cast<RecordDecl>(DC))
700 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
702 N->getQualifiedNameAsString());
703 else if (const auto *N = dyn_cast<FunctionDecl>(DC))
704 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
706 N->getQualifiedNameAsString());
707 else if (const auto *N = dyn_cast<EnumDecl>(DC))
708 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
709 InfoType::IT_enum, N->getQualifiedNameAsString());
710 } while ((DC = DC->getParent()));
711 // The global namespace should be added to the list of namespaces if the decl
712 // corresponds to a Record and if it doesn't have any namespace (because this
713 // means it's in the global namespace). Also if its outermost namespace is a
714 // record because that record matches the previous condition mentioned.
715 if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
716 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
717 Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
719}
720
721static void
722populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
723 const clang::Decl *D) {
724 if (const TemplateParameterList *ParamList =
725 D->getDescribedTemplateParams()) {
726 if (!TemplateInfo) {
727 TemplateInfo.emplace();
728 }
729 for (const NamedDecl *ND : *ParamList) {
730 TemplateInfo->Params.emplace_back(
731 getSourceCode(ND, ND->getSourceRange()));
732 }
733 }
734}
735
736static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,
737 const TemplateArgument &Arg) {
738 // The TemplateArgument's pretty printing handles all the normal cases
739 // well enough for our requirements.
740 std::string Str;
741 llvm::raw_string_ostream Stream(Str);
742 Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
743 return TemplateParamInfo(Str);
744}
745
746template <typename T>
747static void populateInfo(Info &I, const T *D, const FullComment *C,
748 bool &IsInAnonymousNamespace) {
749 I.USR = getUSRForDecl(D);
750 if (auto ConversionDecl = dyn_cast_or_null<CXXConversionDecl>(D);
751 ConversionDecl && ConversionDecl->getConversionType()
752 .getTypePtr()
753 ->isTemplateTypeParmType())
754 I.Name = "operator " + ConversionDecl->getConversionType().getAsString();
755 else
756 I.Name = D->getNameAsString();
757 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
758 if (C) {
759 I.Description.emplace_back();
760 parseFullComment(C, I.Description.back());
761 }
762}
763
764template <typename T>
765static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
766 Location Loc, bool &IsInAnonymousNamespace) {
767 populateInfo(I, D, C, IsInAnonymousNamespace);
768 if (D->isThisDeclarationADefinition())
769 I.DefLoc = Loc;
770 else
771 I.Loc.emplace_back(Loc);
772
773 auto *Mangler = ItaniumMangleContext::create(
774 D->getASTContext(), D->getASTContext().getDiagnostics());
775 std::string MangledName;
776 llvm::raw_string_ostream MangledStream(MangledName);
777 if (auto *CXXD = dyn_cast<CXXRecordDecl>(D))
778 Mangler->mangleCXXVTable(CXXD, MangledStream);
779 else
780 MangledStream << D->getNameAsString();
781 // A 250 length limit was chosen since 255 is a common limit across
782 // different filesystems, with a 5 character buffer for file extensions.
783 if (MangledName.size() > 250) {
784 auto SymbolID = llvm::toStringRef(llvm::toHex(I.USR)).str();
785 I.MangledName = MangledName.substr(0, 250 - SymbolID.size()) + SymbolID;
786 } else
787 I.MangledName = MangledName;
788 delete Mangler;
789}
790
791static void
792handleCompoundConstraints(const Expr *Constraint,
793 std::vector<ConstraintInfo> &ConstraintInfos) {
794 if (Constraint->getStmtClass() == Stmt::ParenExprClass) {
795 handleCompoundConstraints(dyn_cast<ParenExpr>(Constraint)->getSubExpr(),
796 ConstraintInfos);
797 } else if (Constraint->getStmtClass() == Stmt::BinaryOperatorClass) {
798 auto *BinaryOpExpr = dyn_cast<BinaryOperator>(Constraint);
799 handleCompoundConstraints(BinaryOpExpr->getLHS(), ConstraintInfos);
800 handleCompoundConstraints(BinaryOpExpr->getRHS(), ConstraintInfos);
801 } else if (Constraint->getStmtClass() ==
802 Stmt::ConceptSpecializationExprClass) {
803 auto *Concept = dyn_cast<ConceptSpecializationExpr>(Constraint);
804 ConstraintInfo CI(getUSRForDecl(Concept->getNamedConcept()),
805 Concept->getNamedConcept()->getNameAsString());
806 CI.ConstraintExpr = exprToString(Concept);
807 ConstraintInfos.push_back(CI);
808 }
809}
810
811static void populateConstraints(TemplateInfo &I, const TemplateDecl *D) {
812 if (!D || !D->hasAssociatedConstraints())
813 return;
814
815 SmallVector<AssociatedConstraint> AssociatedConstraints;
816 D->getAssociatedConstraints(AssociatedConstraints);
817 for (const auto &Constraint : AssociatedConstraints) {
818 if (!Constraint)
819 continue;
820
821 // TODO: Investigate if atomic constraints need to be handled specifically.
822 if (const auto *ConstraintExpr =
823 dyn_cast_or_null<ConceptSpecializationExpr>(
824 Constraint.ConstraintExpr)) {
825 ConstraintInfo CI(getUSRForDecl(ConstraintExpr->getNamedConcept()),
826 ConstraintExpr->getNamedConcept()->getNameAsString());
827 CI.ConstraintExpr = exprToString(ConstraintExpr);
828 I.Constraints.push_back(std::move(CI));
829 } else {
830 handleCompoundConstraints(Constraint.ConstraintExpr, I.Constraints);
831 }
832 }
833}
834
835static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
836 const FullComment *FC, Location Loc,
837 bool &IsInAnonymousNamespace) {
838 populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
839 auto &LO = D->getLangOpts();
840 I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
842 parseParameters(I, D);
843 I.IsStatic = D->isStatic();
844
846 if (I.Template)
847 populateConstraints(I.Template.value(), D->getDescribedFunctionTemplate());
848
849 // Handle function template specializations.
850 if (const FunctionTemplateSpecializationInfo *FTSI =
851 D->getTemplateSpecializationInfo()) {
852 if (!I.Template)
853 I.Template.emplace();
854 I.Template->Specialization.emplace();
855 auto &Specialization = *I.Template->Specialization;
856
857 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
858
859 // Template parameters to the specialization.
860 if (FTSI->TemplateArguments) {
861 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
862 Specialization.Params.push_back(convertTemplateArgToInfo(D, Arg));
863 }
864 }
865 }
866}
867
868static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) {
869 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
870
871 ASTContext &Context = D->getASTContext();
872 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
873 // of this logic. See also similar code in Mapper.cpp.
874 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
875 if (!Comment)
876 return;
877
878 Comment->setAttached();
879 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
880 I.Description.emplace_back();
881 parseFullComment(Fc, I.Description.back());
882 }
883}
884
885static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
886 const DeclaratorDecl *D, bool IsStatic) {
887 // Use getAccessUnsafe so that we just get the default AS_none if it's not
888 // valid, as opposed to an assert.
889 MemberTypeInfo &NewMember = I.Members.emplace_back(
890 getTypeInfoForType(D->getTypeSourceInfo()->getType(), D->getLangOpts()),
891 D->getNameAsString(),
892 getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic);
893 populateMemberTypeInfo(NewMember, D);
894}
895
896static void
897parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
898 bool PublicOnly, bool IsParent,
899 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
900 // Don't parse bases if this isn't a definition.
901 if (!D->isThisDeclarationADefinition())
902 return;
903 for (const CXXBaseSpecifier &B : D->bases()) {
904 if (const auto *Base = B.getType()->getAsCXXRecordDecl()) {
905 if (Base->isCompleteDefinition()) {
906 // Initialized without USR and name, this will be set in the following
907 // if-else stmt.
909 {}, "", getInfoRelativePath(Base), B.isVirtual(),
910 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
911 IsParent);
912 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
913 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
914 BI.USR = getUSRForDecl(D);
915 BI.Name = B.getType().getAsString();
916 } else {
917 BI.USR = getUSRForDecl(Base);
918 BI.Name = Base->getNameAsString();
919 }
920 parseFields(BI, Base, PublicOnly, BI.Access);
921 for (const auto &Decl : Base->decls())
922 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
923 // Don't serialize private methods
924 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
925 !MD->isUserProvided())
926 continue;
927 FunctionInfo FI;
928 FI.IsMethod = true;
929 FI.IsStatic = MD->isStatic();
930 // The seventh arg in populateFunctionInfo is a boolean passed by
931 // reference, its value is not relevant in here so it's not used
932 // anywhere besides the function call.
933 bool IsInAnonymousNamespace;
934 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},
935 IsInAnonymousNamespace);
936 FI.Access =
937 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
938 BI.Children.Functions.emplace_back(std::move(FI));
939 }
940 I.Bases.emplace_back(std::move(BI));
941 // Call this function recursively to get the inherited classes of
942 // this base; these new bases will also get stored in the original
943 // RecordInfo: I.
944 parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
945 I.Bases.back().Access);
946 }
947 }
948 }
949}
950
951std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
952emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
953 bool PublicOnly) {
954 auto NSI = std::make_unique<NamespaceInfo>();
955 bool IsInAnonymousNamespace = false;
956 populateInfo(*NSI, D, FC, IsInAnonymousNamespace);
957 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
958 return {};
959
960 NSI->Name = D->isAnonymousNamespace()
961 ? llvm::SmallString<16>("@nonymous_namespace")
962 : NSI->Name;
963 NSI->Path = getInfoRelativePath(NSI->Namespace);
964 if (NSI->Namespace.empty() && NSI->USR == SymbolID())
965 return {std::unique_ptr<Info>{std::move(NSI)}, nullptr};
966
967 // Namespaces are inserted into the parent by reference, so we need to return
968 // both the parent and the record itself.
969 return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(*NSI)};
970}
971
972static void parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
973 if (!D->hasDefinition() || !D->hasFriends())
974 return;
975
976 for (const FriendDecl *FD : D->friends()) {
977 if (FD->isUnsupportedFriend())
978 continue;
979
981 const auto *ActualDecl = FD->getFriendDecl();
982 if (!ActualDecl) {
983 const auto *FriendTypeInfo = FD->getFriendType();
984 if (!FriendTypeInfo)
985 continue;
986 ActualDecl = FriendTypeInfo->getType()->getAsCXXRecordDecl();
987
988 if (!ActualDecl)
989 continue;
990 F.IsClass = true;
991 }
992
993 if (const auto *ActualTD = dyn_cast_or_null<TemplateDecl>(ActualDecl)) {
994 if (isa<RecordDecl>(ActualTD->getTemplatedDecl()))
995 F.IsClass = true;
996 F.Template.emplace();
997 for (const auto *Param : ActualTD->getTemplateParameters()->asArray())
998 F.Template->Params.emplace_back(
999 getSourceCode(Param, Param->getSourceRange()));
1000 ActualDecl = ActualTD->getTemplatedDecl();
1001 }
1002
1003 if (auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(ActualDecl)) {
1004 FunctionInfo TempInfo;
1005 parseParameters(TempInfo, FuncDecl);
1006 F.Params.emplace();
1007 F.Params = std::move(TempInfo.Params);
1008 F.ReturnType = getTypeInfoForType(FuncDecl->getReturnType(),
1009 FuncDecl->getLangOpts());
1010 }
1011
1012 F.Ref =
1013 Reference(getUSRForDecl(ActualDecl), ActualDecl->getNameAsString(),
1014 InfoType::IT_default, ActualDecl->getQualifiedNameAsString(),
1015 getInfoRelativePath(ActualDecl));
1016
1017 RI.Friends.push_back(std::move(F));
1018 }
1019}
1020
1021std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1022emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
1023 bool PublicOnly) {
1024
1025 auto RI = std::make_unique<RecordInfo>();
1026 bool IsInAnonymousNamespace = false;
1027
1028 populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);
1029 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1030 return {};
1031
1032 RI->TagType = D->getTagKind();
1033 parseFields(*RI, D, PublicOnly);
1034
1035 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
1036 RI->FullName = getRecordPrototype(C);
1037 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
1038 RI->Name = TD->getNameAsString();
1039 RI->IsTypeDef = true;
1040 }
1041 // TODO: remove first call to parseBases, that function should be deleted
1042 parseBases(*RI, C);
1043 parseBases(*RI, C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true);
1044 parseFriends(*RI, C);
1045 }
1046 RI->Path = getInfoRelativePath(RI->Namespace);
1047
1048 populateTemplateParameters(RI->Template, D);
1049 if (RI->Template)
1050 populateConstraints(RI->Template.value(), D->getDescribedTemplate());
1051
1052 // Full and partial specializations.
1053 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
1054 if (!RI->Template)
1055 RI->Template.emplace();
1056 RI->Template->Specialization.emplace();
1057 auto &Specialization = *RI->Template->Specialization;
1058
1059 // What this is a specialization of.
1060 auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
1061 if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
1062 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
1063 else if (auto *SpecTD =
1064 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
1065 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
1066
1067 // Parameters to the specialization. For partial specializations, get the
1068 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
1069 // because the non-explicit template parameters will have generated internal
1070 // placeholder names rather than the names the user typed that match the
1071 // template parameters.
1072 if (const ClassTemplatePartialSpecializationDecl *CTPSD =
1073 dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
1074 if (const ASTTemplateArgumentListInfo *AsWritten =
1075 CTPSD->getTemplateArgsAsWritten()) {
1076 for (unsigned Idx = 0; Idx < AsWritten->getNumTemplateArgs(); Idx++) {
1077 Specialization.Params.emplace_back(
1078 getSourceCode(D, (*AsWritten)[Idx].getSourceRange()));
1079 }
1080 }
1081 } else {
1082 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
1083 Specialization.Params.push_back(convertTemplateArgToInfo(D, Arg));
1084 }
1085 }
1086 }
1087
1088 // Records are inserted into the parent by reference, so we need to return
1089 // both the parent and the record itself.
1091 return {std::move(RI), std::move(Parent)};
1092}
1093
1094std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1095emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc,
1096 bool PublicOnly) {
1097 FunctionInfo Func;
1098 bool IsInAnonymousNamespace = false;
1099 populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
1100 Func.Access = clang::AccessSpecifier::AS_none;
1101 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1102 return {};
1103
1104 // Info is wrapped in its parent scope so is returned in the second position.
1105 return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
1106}
1107
1108std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1109emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
1110 bool PublicOnly) {
1111 FunctionInfo Func;
1112 bool IsInAnonymousNamespace = false;
1113 populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
1114 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1115 return {};
1116
1117 Func.IsMethod = true;
1118 Func.IsStatic = D->isStatic();
1119
1120 const NamedDecl *Parent = nullptr;
1121 if (const auto *SD =
1122 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
1123 Parent = SD->getSpecializedTemplate();
1124 else
1125 Parent = D->getParent();
1126
1127 SymbolID ParentUSR = getUSRForDecl(Parent);
1128 Func.Parent =
1129 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
1130 Parent->getQualifiedNameAsString()};
1131 Func.Access = D->getAccess();
1132
1133 // Info is wrapped in its parent scope so is returned in the second position.
1134 return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
1135}
1136
1137static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
1138 assert(D && "Invalid Decl when extracting comment");
1139 ASTContext &Context = D->getASTContext();
1140 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
1141 if (!Comment)
1142 return;
1143
1144 Comment->setAttached();
1145 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
1146 Info.Description.emplace_back();
1147 parseFullComment(Fc, Info.Description.back());
1148 }
1149}
1150
1151std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1152emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
1153 bool PublicOnly) {
1155 bool IsInAnonymousNamespace = false;
1156 populateInfo(Info, D, FC, IsInAnonymousNamespace);
1157
1158 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1159 return {};
1160
1161 Info.DefLoc = Loc;
1162 auto &LO = D->getLangOpts();
1163 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1164
1165 if (Info.Underlying.Type.Name.empty()) {
1166 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
1167 // The record serializer explicitly checks for this syntax and constructs
1168 // a record with that name, so we don't want to emit a duplicate here.
1169 return {};
1170 }
1171 Info.IsUsing = false;
1173
1174 // Info is wrapped in its parent scope so is returned in the second position.
1175 return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
1176}
1177
1178// A type alias is a C++ "using" declaration for a type. It gets mapped to a
1179// TypedefInfo with the IsUsing flag set.
1180std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1181emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,
1182 bool PublicOnly) {
1184 bool IsInAnonymousNamespace = false;
1185 populateInfo(Info, D, FC, IsInAnonymousNamespace);
1186 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1187 return {};
1188
1189 Info.DefLoc = Loc;
1190 const LangOptions &LO = D->getLangOpts();
1191 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1192 Info.TypeDeclaration = getTypeAlias(D);
1193 Info.IsUsing = true;
1194
1196
1197 // Info is wrapped in its parent scope so is returned in the second position.
1198 return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
1199}
1200
1201std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1202emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,
1203 bool PublicOnly) {
1204 EnumInfo Enum;
1205 bool IsInAnonymousNamespace = false;
1206 populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace);
1207
1208 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1209 return {};
1210
1211 Enum.Scoped = D->isScoped();
1212 if (D->isFixed()) {
1213 auto Name = D->getIntegerType().getAsString();
1214 Enum.BaseType = TypeInfo(Name, Name);
1215 }
1216 parseEnumerators(Enum, D);
1217
1218 // Info is wrapped in its parent scope so is returned in the second position.
1219 return {nullptr, makeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
1220}
1221
1222std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1223emitInfo(const ConceptDecl *D, const FullComment *FC, const Location &Loc,
1224 bool PublicOnly) {
1225 ConceptInfo Concept;
1226
1227 bool IsInAnonymousNamespace = false;
1228 populateInfo(Concept, D, FC, IsInAnonymousNamespace);
1229 Concept.IsType = D->isTypeConcept();
1230 Concept.DefLoc = Loc;
1231 Concept.ConstraintExpression = exprToString(D->getConstraintExpr());
1232
1233 if (auto *ConceptParams = D->getTemplateParameters()) {
1234 for (const auto *Param : ConceptParams->asArray()) {
1235 Concept.Template.Params.emplace_back(
1236 getSourceCode(Param, Param->getSourceRange()));
1237 }
1238 }
1239
1240 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1241 return {};
1242
1243 return {nullptr, makeAndInsertIntoParent<ConceptInfo &&>(std::move(Concept))};
1244}
1245
1246std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1247emitInfo(const VarDecl *D, const FullComment *FC, const Location &Loc,
1248 bool PublicOnly) {
1249 VarInfo Var;
1250 bool IsInAnonymousNamespace = false;
1251 populateSymbolInfo(Var, D, FC, Loc, IsInAnonymousNamespace);
1252 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1253 return {};
1254
1255 if (D->getStorageClass() == StorageClass::SC_Static)
1256 Var.IsStatic = true;
1257 Var.Type =
1258 getTypeInfoForType(D->getType(), D->getASTContext().getPrintingPolicy());
1259
1260 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1261 return {};
1262
1263 return {nullptr, makeAndInsertIntoParent<VarInfo &&>(std::move(Var))};
1264}
1265
1266} // namespace serialize
1267} // namespace doc
1268} // 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 llvm::SmallString< 16 > getRecordPrototype(const CXXRecordDecl *CXXRD)
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[]