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