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