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 getTransientArena().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, getTransientArena());
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, getTransientArena());
278 }
279 if (!AttrValues.empty()) {
280 CurrentCI.AttrValues = allocateArray(AttrValues, getTransientArena());
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, getTransientArena());
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(cast<NamespaceInfo>(I), Diags);
368 return serialize(cast<RecordInfo>(I), Diags);
370 return serialize(cast<EnumInfo>(I), Diags);
372 return serialize(cast<FunctionInfo>(I), Diags);
374 return serialize(cast<ConceptInfo>(I), Diags);
376 return serialize(cast<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, getTransientArena());
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, getTransientArena());
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, getTransientArena());
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, getTransientArena());
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, getTransientArena());
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, getTransientArena());
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 =
794 if (C) {
795
797 I.Description.push_back(*NewCI);
798 parseFullComment(C, *NewCI->Ptr);
799 }
800}
801
802template <typename T>
803void Serializer::populateSymbolInfo(SymbolInfo &I, const T *D,
804 const FullComment *C, Location Loc,
805 bool &IsInAnonymousNamespace) {
806 populateInfo(I, D, C, IsInAnonymousNamespace);
807 if (D->isThisDeclarationADefinition())
808 I.DefLoc = Loc;
809 else {
810 I.Loc.push_back(*allocateListNodeTransient<Location>(Loc));
811 }
812
813 auto *Mangler = ItaniumMangleContext::create(
814 D->getASTContext(), D->getASTContext().getDiagnostics());
815 std::string MangledName;
816 llvm::raw_string_ostream MangledStream(MangledName);
817 if (auto *CXXD = dyn_cast<CXXRecordDecl>(D))
818 Mangler->mangleCXXVTable(CXXD, MangledStream);
819 else
820 MangledStream << D->getNameAsString();
821 // A 250 length limit was chosen since 255 is a common limit across
822 // different filesystems, with a 5 character buffer for file extensions.
823 if (MangledName.size() > 250) {
824 auto SymbolID = llvm::toStringRef(llvm::toHex(I.USR)).str();
825 I.MangledName =
826 internString(MangledName.substr(0, 250 - SymbolID.size()) + SymbolID);
827 } else
828 I.MangledName = internString(MangledName);
829 delete Mangler;
830}
831
832void Serializer::handleCompoundConstraints(
833 const Expr *Constraint,
834 llvm::SmallVectorImpl<ConstraintInfo> &ConstraintInfos) {
835 if (Constraint->getStmtClass() == Stmt::ParenExprClass) {
836 handleCompoundConstraints(dyn_cast<ParenExpr>(Constraint)->getSubExpr(),
837 ConstraintInfos);
838 } else if (Constraint->getStmtClass() == Stmt::BinaryOperatorClass) {
839 auto *BinaryOpExpr = dyn_cast<BinaryOperator>(Constraint);
840 handleCompoundConstraints(BinaryOpExpr->getLHS(), ConstraintInfos);
841 handleCompoundConstraints(BinaryOpExpr->getRHS(), ConstraintInfos);
842 } else if (Constraint->getStmtClass() ==
843 Stmt::ConceptSpecializationExprClass) {
844 auto *Concept = dyn_cast<ConceptSpecializationExpr>(Constraint);
845 ConstraintInfo CI(getUSRForDecl(Concept->getNamedConcept()),
846 Concept->getNamedConcept()->getNameAsString());
847 CI.ConstraintExpr = internString(exprToString(Concept));
848 ConstraintInfos.push_back(CI);
849 }
850}
851
852void Serializer::populateConstraints(TemplateInfo &I, const TemplateDecl *D) {
853 if (!D || !D->hasAssociatedConstraints())
854 return;
855
856 SmallVector<AssociatedConstraint> AssociatedConstraints;
857 D->getAssociatedConstraints(AssociatedConstraints);
858 SmallVector<ConstraintInfo, 4> LocalConstraints;
859 for (const auto &Constraint : AssociatedConstraints) {
860 if (!Constraint)
861 continue;
862
863 // TODO: Investigate if atomic constraints need to be handled specifically.
864 if (const auto *ConstraintExpr =
865 dyn_cast_or_null<ConceptSpecializationExpr>(
866 Constraint.ConstraintExpr)) {
867 ConstraintInfo CI(getUSRForDecl(ConstraintExpr->getNamedConcept()),
868 ConstraintExpr->getNamedConcept()->getNameAsString());
869 CI.ConstraintExpr = internString(exprToString(ConstraintExpr));
870 LocalConstraints.push_back(std::move(CI));
871 } else {
872 handleCompoundConstraints(Constraint.ConstraintExpr, LocalConstraints);
873 }
874 }
875 if (!LocalConstraints.empty())
876 I.Constraints =
878}
879
880void Serializer::populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
881 const FullComment *FC, Location Loc,
882 bool &IsInAnonymousNamespace) {
883 populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
884 auto &LO = D->getLangOpts();
885 I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
886 I.Prototype = getFunctionPrototype(D);
887 parseParameters(I, D);
888 I.IsStatic = D->isStatic();
889
890 populateTemplateParameters(I.Template, D);
891 if (I.Template)
892 populateConstraints(I.Template.value(), D->getDescribedFunctionTemplate());
893
894 // Handle function template specializations.
895 if (const FunctionTemplateSpecializationInfo *FTSI =
896 D->getTemplateSpecializationInfo()) {
897 if (!I.Template)
898 I.Template.emplace();
899 I.Template->Specialization.emplace();
900 auto &Specialization = *I.Template->Specialization;
901
902 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
903
904 // Template parameters to the specialization.
905 if (FTSI->TemplateArguments) {
906 SmallVector<TemplateParamInfo, 4> LocalParams;
907 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
908 LocalParams.push_back(convertTemplateArgToInfo(D, Arg));
909 }
910 if (!LocalParams.empty())
911 Specialization.Params =
913 }
914 }
915}
916
917// TODO: Rename this, since this doesn't populate anything besides comments and
918// isn't exclusive to members
919template <typename T>
920void Serializer::populateMemberTypeInfo(T &I, const Decl *D) {
921 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
922
923 ASTContext &Context = D->getASTContext();
924 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
925 // of this logic. See also similar code in Mapper.cpp.
926 RawComment *Comment = Context.getRawCommentNoCache(D);
927 if (!Comment)
928 return;
929
930 Comment->setAttached();
931 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
933 I.Description.push_back(*NewCI);
934 parseFullComment(Fc, *NewCI->Ptr);
935 }
936}
937
938void Serializer::populateMemberTypeInfo(
939 SmallVectorImpl<MemberTypeInfo> &Members, AccessSpecifier &Access,
940 const DeclaratorDecl *D, bool IsStatic) {
941 // Use getAccessUnsafe so that we just get the default AS_none if it's not
942 // valid, as opposed to an assert.
943 MemberTypeInfo &NewMember = Members.emplace_back(
944 getTypeInfoForType(D->getTypeSourceInfo()->getType(), D->getLangOpts()),
945 D->getNameAsString(),
946 getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic);
947 populateMemberTypeInfo(NewMember, D);
948}
949
950void Serializer::parseBases(llvm::SmallVectorImpl<BaseRecordInfo> &Bases,
951 const CXXRecordDecl *D, bool IsFileInRootDir,
952 bool PublicOnly, bool IsParent,
953 AccessSpecifier ParentAccess) {
954 // Don't parse bases if this isn't a definition.
955 if (!D->isThisDeclarationADefinition())
956 return;
957 for (const CXXBaseSpecifier &B : D->bases()) {
958 if (const auto *Base = B.getType()->getAsCXXRecordDecl()) {
959 if (Base->isCompleteDefinition()) {
960 // Initialized without USR and name, this will be set in the following
961 // if-else stmt.
962 BaseRecordInfo BI(
963 {}, "", internString(getInfoRelativePath(Base)), B.isVirtual(),
964 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
965 IsParent);
966 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
967 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
968 BI.USR = getUSRForDecl(D);
969 BI.Name = internString(B.getType().getAsString());
970 } else {
971 BI.USR = getUSRForDecl(Base);
972 BI.Name = internString(Base->getNameAsString());
973 }
974 parseFields(BI, Base, PublicOnly, BI.Access);
975 for (const auto &Decl : Base->decls())
976 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
977 // Don't serialize private methods
978 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
979 !MD->isUserProvided())
980 continue;
981 FunctionInfo FI;
982 FI.IsMethod = true;
983 FI.IsStatic = MD->isStatic();
984 // The seventh arg in populateFunctionInfo is a boolean passed by
985 // reference, its value is not relevant in here so it's not used
986 // anywhere besides the function call.
987 bool IsInAnonymousNamespace;
988 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},
989 IsInAnonymousNamespace);
990 FI.Access =
991 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
992 BI.Children.Functions.push_back(
994 }
995 Bases.emplace_back(std::move(BI));
996 // Call this function recursively to get the inherited classes of
997 // this base; these new bases will also get stored in the original
998 // RecordInfo: I.
999 parseBases(Bases, Base, IsFileInRootDir, PublicOnly, false,
1000 Bases.back().Access);
1001 }
1002 }
1003 }
1004}
1005
1006std::pair<Info *, Info *> Serializer::emitInfo(const NamespaceDecl *D,
1007 const FullComment *FC,
1008 Location Loc, bool PublicOnly) {
1010 bool IsInAnonymousNamespace = false;
1011 populateInfo(*NSI, D, FC, IsInAnonymousNamespace);
1012 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1013 return {};
1014
1015 NSI->Name = D->isAnonymousNamespace() ? "@nonymous_namespace" : NSI->Name;
1016 NSI->Path = getInfoRelativePath(NSI->Namespace);
1017 if (NSI->Namespace.empty() && NSI->USR == SymbolID())
1018 return {NSI, nullptr};
1019
1020 // Namespaces are inserted into the parent by reference, so we need to return
1021 // both the parent and the record itself.
1022 return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(*NSI)};
1023}
1024
1025void Serializer::parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
1026 if (!D->hasDefinition() || !D->hasFriends())
1027 return;
1028
1029 llvm::SmallVector<FriendInfo, 4> LocalFriends;
1030
1031 for (const FriendDecl *FD : D->friends()) {
1032 if (FD->isUnsupportedFriend())
1033 continue;
1034
1035 FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));
1036 const auto *ActualDecl = FD->getFriendDecl();
1037 if (!ActualDecl) {
1038 const auto *FriendTypeInfo = FD->getFriendType();
1039 if (!FriendTypeInfo)
1040 continue;
1041 ActualDecl = FriendTypeInfo->getType()->getAsCXXRecordDecl();
1042
1043 if (!ActualDecl)
1044 continue;
1045 F.IsClass = true;
1046 }
1047
1048 if (const auto *ActualTD = dyn_cast_or_null<TemplateDecl>(ActualDecl)) {
1049 if (isa<RecordDecl>(ActualTD->getTemplatedDecl()))
1050 F.IsClass = true;
1051 F.Template.emplace();
1052 llvm::SmallVector<TemplateParamInfo, 4> LocalParams;
1053 for (const auto *Param : ActualTD->getTemplateParameters()->asArray())
1054 LocalParams.emplace_back(getSourceCode(Param, Param->getSourceRange()));
1055 if (!LocalParams.empty())
1056 F.Template->Params =
1058 ActualDecl = ActualTD->getTemplatedDecl();
1059 }
1060
1061 if (auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(ActualDecl)) {
1062 FunctionInfo TempInfo;
1063 parseParameters(TempInfo, FuncDecl);
1064 F.Params =
1066 F.ReturnType = getTypeInfoForType(FuncDecl->getReturnType(),
1067 FuncDecl->getLangOpts());
1068 }
1069
1070 F.Ref =
1071 Reference(getUSRForDecl(ActualDecl), ActualDecl->getNameAsString(),
1072 InfoType::IT_default, ActualDecl->getQualifiedNameAsString(),
1073 getInfoRelativePath(ActualDecl));
1074
1075 populateMemberTypeInfo(F, ActualDecl);
1076 LocalFriends.push_back(std::move(F));
1077 }
1078 if (!LocalFriends.empty())
1080}
1081
1082std::pair<Info *, Info *> Serializer::emitInfo(const RecordDecl *D,
1083 const FullComment *FC,
1084 Location Loc, bool PublicOnly) {
1085
1086 auto *RI = allocateTransient<RecordInfo>();
1087 bool IsInAnonymousNamespace = false;
1088
1089 populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);
1090 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1091 return {};
1092
1093 RI->TagType = D->getTagKind();
1094 parseFields(*RI, D, PublicOnly);
1095
1096 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
1097 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
1098 RI->Name = internString(TD->getNameAsString());
1099 RI->IsTypeDef = true;
1100 }
1101 // TODO: remove first call to parseBases, that function should be deleted
1102 parseBases(*RI, C);
1103 llvm::SmallVector<BaseRecordInfo, 4> LocalBases;
1104 parseBases(LocalBases, C, /*IsFileInRootDir=*/true, PublicOnly,
1105 /*IsParent=*/true);
1106 if (!LocalBases.empty())
1107 RI->Bases =
1109 parseFriends(*RI, C);
1110 }
1111 RI->Path = internString(getInfoRelativePath(RI->Namespace));
1112
1113 populateTemplateParameters(RI->Template, D);
1114 if (RI->Template)
1115 populateConstraints(RI->Template.value(), D->getDescribedTemplate());
1116
1117 // Full and partial specializations.
1118 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
1119 if (!RI->Template)
1120 RI->Template.emplace();
1121 RI->Template->Specialization.emplace();
1122 auto &Specialization = *RI->Template->Specialization;
1123
1124 // What this is a specialization of.
1125 auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
1126 if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
1127 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
1128 else if (auto *SpecTD =
1129 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
1130 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
1131
1132 // Parameters to the specialization. For partial specializations, get the
1133 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
1134 // because the non-explicit template parameters will have generated internal
1135 // placeholder names rather than the names the user typed that match the
1136 // template parameters.
1137 if (const ClassTemplatePartialSpecializationDecl *CTPSD =
1138 dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
1139 if (const ASTTemplateArgumentListInfo *AsWritten =
1140 CTPSD->getTemplateArgsAsWritten()) {
1141 llvm::SmallVector<TemplateParamInfo, 4> LocalParams;
1142 for (unsigned Idx = 0; Idx < AsWritten->getNumTemplateArgs(); Idx++) {
1143 LocalParams.emplace_back(
1144 getSourceCode(D, (*AsWritten)[Idx].getSourceRange()));
1145 }
1146 if (!LocalParams.empty())
1147 Specialization.Params = allocateArray<TemplateParamInfo>(
1148 LocalParams, getTransientArena());
1149 }
1150 } else {
1151 llvm::SmallVector<TemplateParamInfo, 4> LocalParams;
1152 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
1153 LocalParams.push_back(convertTemplateArgToInfo(D, Arg));
1154 }
1155 if (!LocalParams.empty())
1156 Specialization.Params =
1158 }
1159 }
1160
1161 // Records are inserted into the parent by reference, so we need to return
1162 // both the parent and the record itself.
1163 auto *Parent = makeAndInsertIntoParent<const RecordInfo &>(*RI);
1164 return {std::move(RI), std::move(Parent)};
1165}
1166
1167std::pair<Info *, Info *> Serializer::emitInfo(const FunctionDecl *D,
1168 const FullComment *FC,
1169 Location Loc, bool PublicOnly) {
1171 bool IsInAnonymousNamespace = false;
1172 populateFunctionInfo(*Func, D, FC, Loc, IsInAnonymousNamespace);
1173 Func->Access = clang::AccessSpecifier::AS_none;
1174 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1175 return {};
1176
1177 // Info is wrapped in its parent scope so is returned in the second position.
1178 return {nullptr, makeAndInsertIntoParent(*Func)};
1179}
1180
1181std::pair<Info *, Info *> Serializer::emitInfo(const CXXMethodDecl *D,
1182 const FullComment *FC,
1183 Location Loc, bool PublicOnly) {
1185 bool IsInAnonymousNamespace = false;
1186 populateFunctionInfo(*Func, D, FC, Loc, IsInAnonymousNamespace);
1187 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1188 return {};
1189
1190 Func->IsMethod = true;
1191 Func->IsStatic = D->isStatic();
1192
1193 const NamedDecl *Parent = nullptr;
1194 if (const auto *SD =
1195 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
1196 Parent = SD->getSpecializedTemplate();
1197 else
1198 Parent = D->getParent();
1199
1200 SymbolID ParentUSR = getUSRForDecl(Parent);
1201 Func->Parent =
1202 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
1203 Parent->getQualifiedNameAsString()};
1204 Func->Access = D->getAccess();
1205
1206 // Info is wrapped in its parent scope so is returned in the second position.
1207 return {nullptr, makeAndInsertIntoParent(*Func)};
1208}
1209
1210void Serializer::extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
1211 assert(D && "Invalid Decl when extracting comment");
1212 ASTContext &Context = D->getASTContext();
1213 RawComment *Comment = Context.getRawCommentNoCache(D);
1214 if (!Comment)
1215 return;
1216
1217 Comment->setAttached();
1218 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
1220 Info.Description.push_back(*NewCI);
1221 parseFullComment(Fc, *NewCI->Ptr);
1222 }
1223}
1224
1225std::pair<Info *, Info *> Serializer::emitInfo(const TypedefDecl *D,
1226 const FullComment *FC,
1227 Location Loc, bool PublicOnly) {
1229 bool IsInAnonymousNamespace = false;
1230 populateInfo(*Info, D, FC, IsInAnonymousNamespace);
1231
1232 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1233 return {};
1234
1235 Info->DefLoc = Loc;
1236 auto &LO = D->getLangOpts();
1237 Info->Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1238 populateTemplateParameters(Info->Template, D);
1239 if (Info->Template)
1240 populateConstraints(Info->Template.value(), D->getDescribedTemplate());
1241
1242 if (Info->Underlying.Type.Name.empty()) {
1243 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
1244 // The record serializer explicitly checks for this syntax and constructs
1245 // a record with that name, so we don't want to emit a duplicate here.
1246 return {};
1247 }
1248 Info->IsUsing = false;
1249 extractCommentFromDecl(D, *Info);
1250
1251 // Info is wrapped in its parent scope so is returned in the second position.
1252 return {nullptr, makeAndInsertIntoParent(*Info)};
1253}
1254
1255// A type alias is a C++ "using" declaration for a type. It gets mapped to a
1256// TypedefInfo with the IsUsing flag set.
1257std::pair<Info *, Info *> Serializer::emitInfo(const TypeAliasDecl *D,
1258 const FullComment *FC,
1259 Location Loc, bool PublicOnly) {
1261 bool IsInAnonymousNamespace = false;
1262 populateInfo(*Info, D, FC, IsInAnonymousNamespace);
1263 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1264 return {};
1265
1266 Info->DefLoc = Loc;
1267 const LangOptions &LO = D->getLangOpts();
1268 Info->Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1269 Info->TypeDeclaration = getTypeAlias(D);
1270 Info->IsUsing = true;
1271 populateTemplateParameters(Info->Template, D);
1272 if (Info->Template)
1273 populateConstraints(Info->Template.value(), D->getDescribedAliasTemplate());
1274
1275 extractCommentFromDecl(D, *Info);
1276
1277 // Info is wrapped in its parent scope so is returned in the second position.
1278 return {nullptr, makeAndInsertIntoParent(*Info)};
1279}
1280
1281std::pair<Info *, Info *> Serializer::emitInfo(const EnumDecl *D,
1282 const FullComment *FC,
1283 Location Loc, bool PublicOnly) {
1285 bool IsInAnonymousNamespace = false;
1286 populateSymbolInfo(*Enum, D, FC, Loc, IsInAnonymousNamespace);
1287
1288 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1289 return {};
1290
1291 Enum->Scoped = D->isScoped();
1292 if (const TypeSourceInfo *TSI = D->getIntegerTypeSourceInfo()) {
1293 auto Name = TSI->getType().getAsString();
1294 Enum->BaseType = TypeInfo(Name, Name);
1295 }
1296 parseEnumerators(*Enum, D);
1297
1298 // Info is wrapped in its parent scope so is returned in the second position.
1299 return {nullptr, makeAndInsertIntoParent(*Enum)};
1300}
1301
1302std::pair<Info *, Info *> Serializer::emitInfo(const ConceptDecl *D,
1303 const FullComment *FC,
1304 const Location &Loc,
1305 bool PublicOnly) {
1307
1308 bool IsInAnonymousNamespace = false;
1309 populateInfo(*Concept, D, FC, IsInAnonymousNamespace);
1310 Concept->IsType = D->isTypeConcept();
1311 Concept->DefLoc = Loc;
1312 Concept->ConstraintExpression = exprToString(D->getConstraintExpr());
1313
1314 if (auto *ConceptParams = D->getTemplateParameters()) {
1315 llvm::SmallVector<TemplateParamInfo, 4> LocalParams;
1316 for (const auto *Param : ConceptParams->asArray()) {
1317 LocalParams.emplace_back(getSourceCode(Param, Param->getSourceRange()));
1318 }
1319 if (!LocalParams.empty())
1320 Concept->Template.Params =
1322 }
1323
1324 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1325 return {};
1326
1327 return {nullptr, makeAndInsertIntoParent(*Concept)};
1328}
1329
1330std::pair<Info *, Info *> Serializer::emitInfo(const VarDecl *D,
1331 const FullComment *FC,
1332 const Location &Loc,
1333 bool PublicOnly) {
1335 bool IsInAnonymousNamespace = false;
1336 populateSymbolInfo(*Var, D, FC, Loc, IsInAnonymousNamespace);
1337 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1338 return {};
1339
1340 if (D->getStorageClass() == StorageClass::SC_Static)
1341 Var->IsStatic = true;
1342 Var->Type =
1343 getTypeInfoForType(D->getType(), D->getASTContext().getPrintingPolicy());
1344
1345 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1346 return {};
1347
1348 return {nullptr, makeAndInsertIntoParent(*Var)};
1349}
1350
1351} // namespace serialize
1352} // namespace doc
1353} // 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
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
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)
CommentKind stringToCommentKind(llvm::StringRef KindStr)
StringRef internString(const Twine &T)
llvm::ArrayRef< T > allocateArray(llvm::SmallVectorImpl< T > &V, llvm::BumpPtrAllocator &Alloc)
InfoNode< T > * allocateListNodeTransient(Args &&...args)
llvm::BumpPtrAllocator & getTransientArena()
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[]