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