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#include "clang/AST/Attr.h"
12#include "clang/AST/Comment.h"
13#include "clang/Index/USRGeneration.h"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/StringExtras.h"
16#include "llvm/Support/SHA1.h"
17
18using clang::comments::FullComment;
19
20namespace clang {
21namespace doc {
22namespace serialize {
23
24SymbolID hashUSR(llvm::StringRef USR) {
25 return llvm::SHA1::hash(Data: arrayRefFromStringRef(Input: USR));
26}
27
28template <typename T>
29static void
30populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
31 const T *D, bool &IsAnonymousNamespace);
32
33static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D);
34static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
35 const DeclaratorDecl *D,
36 bool IsStatic = false);
37
38static void getTemplateParameters(const TemplateParameterList *TemplateParams,
39 llvm::raw_ostream &Stream) {
40 Stream << "template <";
41
42 for (unsigned i = 0; i < TemplateParams->size(); ++i) {
43 if (i > 0)
44 Stream << ", ";
45
46 const NamedDecl *Param = TemplateParams->getParam(Idx: i);
47 if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Val: Param)) {
48 if (TTP->wasDeclaredWithTypename())
49 Stream << "typename";
50 else
51 Stream << "class";
52 if (TTP->isParameterPack())
53 Stream << "...";
54 Stream << " " << TTP->getNameAsString();
55
56 // We need to also handle type constraints for code like:
57 // template <class T = void>
58 // class C {};
59 if (TTP->hasTypeConstraint()) {
60 Stream << " = ";
61 TTP->getTypeConstraint()->print(
62 OS&: Stream, Policy: TTP->getASTContext().getPrintingPolicy());
63 }
64 } else if (const auto *NTTP =
65 llvm::dyn_cast<NonTypeTemplateParmDecl>(Val: Param)) {
66 NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy());
67 if (NTTP->isParameterPack())
68 Stream << "...";
69 Stream << " " << NTTP->getNameAsString();
70 } else if (const auto *TTPD =
71 llvm::dyn_cast<TemplateTemplateParmDecl>(Val: Param)) {
72 Stream << "template <";
73 getTemplateParameters(TTPD->getTemplateParameters(), Stream);
74 Stream << "> class " << TTPD->getNameAsString();
75 }
76 }
77
78 Stream << "> ";
79}
80
81// Extract the full function prototype from a FunctionDecl including
82// Full Decl
83static llvm::SmallString<256>
84getFunctionPrototype(const FunctionDecl *FuncDecl) {
85 llvm::SmallString<256> Result;
86 llvm::raw_svector_ostream Stream(Result);
87 const ASTContext &Ctx = FuncDecl->getASTContext();
88 const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Val: FuncDecl);
89 // If it's a templated function, handle the template parameters
90 if (const auto *TmplDecl = FuncDecl->getDescribedTemplate())
91 getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
92
93 // If it's a virtual method
94 if (Method && Method->isVirtual())
95 Stream << "virtual ";
96
97 // Print return type
98 FuncDecl->getReturnType().print(OS&: Stream, Policy: Ctx.getPrintingPolicy());
99
100 // Print function name
101 Stream << " " << FuncDecl->getNameAsString() << "(";
102
103 // Print parameter list with types, names, and default values
104 for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) {
105 if (I > 0)
106 Stream << ", ";
107 const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(i: I);
108 QualType ParamType = ParamDecl->getType();
109 ParamType.print(OS&: Stream, Policy: Ctx.getPrintingPolicy());
110
111 // Print parameter name if it has one
112 if (!ParamDecl->getName().empty())
113 Stream << " " << ParamDecl->getNameAsString();
114
115 // Print default argument if it exists
116 if (ParamDecl->hasDefaultArg() &&
117 !ParamDecl->hasUninstantiatedDefaultArg()) {
118 if (const Expr *DefaultArg = ParamDecl->getDefaultArg()) {
119 Stream << " = ";
120 DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy());
121 }
122 }
123 }
124
125 // If it is a variadic function, add '...'
126 if (FuncDecl->isVariadic()) {
127 if (FuncDecl->getNumParams() > 0)
128 Stream << ", ";
129 Stream << "...";
130 }
131
132 Stream << ")";
133
134 // If it's a const method, add 'const' qualifier
135 if (Method) {
136 if (Method->isDeleted())
137 Stream << " = delete";
138 if (Method->size_overridden_methods())
139 Stream << " override";
140 if (Method->hasAttr<clang::FinalAttr>())
141 Stream << " final";
142 if (Method->isConst())
143 Stream << " const";
144 if (Method->isPureVirtual())
145 Stream << " = 0";
146 }
147
148 if (auto ExceptionSpecType = FuncDecl->getExceptionSpecType())
149 Stream << " " << ExceptionSpecType;
150
151 return Result; // Convert SmallString to std::string for return
152}
153
154static llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) {
155 llvm::SmallString<16> Result;
156 llvm::raw_svector_ostream Stream(Result);
157 const ASTContext &Ctx = Alias->getASTContext();
158 if (const auto *TmplDecl = Alias->getDescribedTemplate())
159 getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
160 Stream << "using " << Alias->getNameAsString() << " = ";
161 QualType Q = Alias->getUnderlyingType();
162 Q.print(OS&: Stream, Policy: Ctx.getPrintingPolicy());
163
164 return Result;
165}
166
167// extract full syntax for record declaration
168static llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
169 llvm::SmallString<16> Result;
170 LangOptions LangOpts;
171 PrintingPolicy Policy(LangOpts);
172 Policy.SuppressTagKeyword = false;
173 Policy.FullyQualifiedName = true;
174 Policy.IncludeNewlines = false;
175 llvm::raw_svector_ostream OS(Result);
176 if (const auto *TD = CXXRD->getDescribedClassTemplate()) {
177 OS << "template <";
178 bool FirstParam = true;
179 for (const auto *Param : *TD->getTemplateParameters()) {
180 if (!FirstParam)
181 OS << ", ";
182 Param->print(OS, Policy);
183 FirstParam = false;
184 }
185 OS << ">\n";
186 }
187
188 if (CXXRD->isStruct())
189 OS << "struct ";
190 else if (CXXRD->isClass())
191 OS << "class ";
192 else if (CXXRD->isUnion())
193 OS << "union ";
194
195 OS << CXXRD->getNameAsString();
196
197 // We need to make sure we have a good enough declaration to check. In the
198 // case where the class is a forward declaration, we'll fail assertions in
199 // DeclCXX.
200 if (CXXRD->isCompleteDefinition() && CXXRD->getNumBases() > 0) {
201 OS << " : ";
202 bool FirstBase = true;
203 for (const auto &Base : CXXRD->bases()) {
204 if (!FirstBase)
205 OS << ", ";
206 if (Base.isVirtual())
207 OS << "virtual ";
208 OS << getAccessSpelling(AS: Base.getAccessSpecifier()) << " ";
209 OS << Base.getType().getAsString(Policy);
210 FirstBase = false;
211 }
212 }
213 return Result;
214}
215
216// A function to extract the appropriate relative path for a given info's
217// documentation. The path returned is a composite of the parent namespaces.
218//
219// Example: Given the below, the directory path for class C info will be
220// <root>/A/B
221//
222// namespace A {
223// namespace B {
224//
225// class C {};
226//
227// }
228// }
229static llvm::SmallString<128>
230getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
231 llvm::SmallString<128> Path;
232 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
233 llvm::sys::path::append(path&: Path, a: R->Name);
234 return Path;
235}
236
237static llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
238 llvm::SmallVector<Reference, 4> Namespaces;
239 // The third arg in populateParentNamespaces is a boolean passed by reference,
240 // its value is not relevant in here so it's not used anywhere besides the
241 // function call
242 bool B = true;
243 populateParentNamespaces(Namespaces, D, IsInAnonymousNamespace&: B);
244 return getInfoRelativePath(Namespaces);
245}
246
247class ClangDocCommentVisitor
248 : public ConstCommentVisitor<ClangDocCommentVisitor> {
249public:
250 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
251
252 void parseComment(const comments::Comment *C);
253
254 void visitTextComment(const TextComment *C);
255 void visitInlineCommandComment(const InlineCommandComment *C);
256 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
257 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
258 void visitBlockCommandComment(const BlockCommandComment *C);
259 void visitParamCommandComment(const ParamCommandComment *C);
260 void visitTParamCommandComment(const TParamCommandComment *C);
261 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
262 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
263 void visitVerbatimLineComment(const VerbatimLineComment *C);
264
265private:
266 std::string getCommandName(unsigned CommandID) const;
267 bool isWhitespaceOnly(StringRef S) const;
268
269 CommentInfo &CurrentCI;
270};
271
272void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
273 CurrentCI.Kind = stringToCommentKind(KindStr: C->getCommentKindName());
274 ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
275 for (comments::Comment *Child :
276 llvm::make_range(x: C->child_begin(), y: C->child_end())) {
277 CurrentCI.Children.emplace_back(args: std::make_unique<CommentInfo>());
278 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
279 Visitor.parseComment(C: Child);
280 }
281}
282
283void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
284 if (!isWhitespaceOnly(S: C->getText()))
285 CurrentCI.Text = C->getText();
286}
287
288void ClangDocCommentVisitor::visitInlineCommandComment(
289 const InlineCommandComment *C) {
290 CurrentCI.Name = getCommandName(CommandID: C->getCommandID());
291 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
292 CurrentCI.Args.push_back(Elt: C->getArgText(Idx: I));
293}
294
295void ClangDocCommentVisitor::visitHTMLStartTagComment(
296 const HTMLStartTagComment *C) {
297 CurrentCI.Name = C->getTagName();
298 CurrentCI.SelfClosing = C->isSelfClosing();
299 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
300 const HTMLStartTagComment::Attribute &Attr = C->getAttr(Idx: I);
301 CurrentCI.AttrKeys.push_back(Elt: Attr.Name);
302 CurrentCI.AttrValues.push_back(Elt: Attr.Value);
303 }
304}
305
306void ClangDocCommentVisitor::visitHTMLEndTagComment(
307 const HTMLEndTagComment *C) {
308 CurrentCI.Name = C->getTagName();
309 CurrentCI.SelfClosing = true;
310}
311
312void ClangDocCommentVisitor::visitBlockCommandComment(
313 const BlockCommandComment *C) {
314 CurrentCI.Name = getCommandName(CommandID: C->getCommandID());
315 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
316 CurrentCI.Args.push_back(Elt: C->getArgText(Idx: I));
317}
318
319void ClangDocCommentVisitor::visitParamCommandComment(
320 const ParamCommandComment *C) {
321 CurrentCI.Direction =
322 ParamCommandComment::getDirectionAsString(D: C->getDirection());
323 CurrentCI.Explicit = C->isDirectionExplicit();
324 if (C->hasParamName())
325 CurrentCI.ParamName = C->getParamNameAsWritten();
326}
327
328void ClangDocCommentVisitor::visitTParamCommandComment(
329 const TParamCommandComment *C) {
330 if (C->hasParamName())
331 CurrentCI.ParamName = C->getParamNameAsWritten();
332}
333
334void ClangDocCommentVisitor::visitVerbatimBlockComment(
335 const VerbatimBlockComment *C) {
336 CurrentCI.Name = getCommandName(CommandID: C->getCommandID());
337 CurrentCI.CloseName = C->getCloseName();
338}
339
340void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
341 const VerbatimBlockLineComment *C) {
342 if (!isWhitespaceOnly(S: C->getText()))
343 CurrentCI.Text = C->getText();
344}
345
346void ClangDocCommentVisitor::visitVerbatimLineComment(
347 const VerbatimLineComment *C) {
348 if (!isWhitespaceOnly(S: C->getText()))
349 CurrentCI.Text = C->getText();
350}
351
352bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
353 return llvm::all_of(Range&: S, P: isspace);
354}
355
356std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
357 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
358 if (Info)
359 return Info->Name;
360 // TODO: Add parsing for \file command.
361 return "<not a builtin command>";
362}
363
364// Serializing functions.
365
366static std::string getSourceCode(const Decl *D, const SourceRange &R) {
367 return Lexer::getSourceText(Range: CharSourceRange::getTokenRange(R),
368 SM: D->getASTContext().getSourceManager(),
369 LangOpts: D->getASTContext().getLangOpts())
370 .str();
371}
372
373template <typename T> static std::string serialize(T &I) {
374 SmallString<2048> Buffer;
375 llvm::BitstreamWriter Stream(Buffer);
376 ClangDocBitcodeWriter Writer(Stream);
377 Writer.emitBlock(I);
378 return Buffer.str().str();
379}
380
381std::string serialize(std::unique_ptr<Info> &I) {
382 switch (I->IT) {
383 case InfoType::IT_namespace:
384 return serialize(I&: *static_cast<NamespaceInfo *>(I.get()));
385 case InfoType::IT_record:
386 return serialize(I&: *static_cast<RecordInfo *>(I.get()));
387 case InfoType::IT_enum:
388 return serialize(I&: *static_cast<EnumInfo *>(I.get()));
389 case InfoType::IT_function:
390 return serialize(I&: *static_cast<FunctionInfo *>(I.get()));
391 default:
392 return "";
393 }
394}
395
396static void parseFullComment(const FullComment *C, CommentInfo &CI) {
397 ClangDocCommentVisitor Visitor(CI);
398 Visitor.parseComment(C);
399}
400
401static SymbolID getUSRForDecl(const Decl *D) {
402 llvm::SmallString<128> USR;
403 if (index::generateUSRForDecl(D, Buf&: USR))
404 return SymbolID();
405 return hashUSR(USR);
406}
407
408static TagDecl *getTagDeclForType(const QualType &T) {
409 if (const TagDecl *D = T->getAsTagDecl())
410 return D->getDefinition();
411 return nullptr;
412}
413
414static RecordDecl *getRecordDeclForType(const QualType &T) {
415 if (const RecordDecl *D = T->getAsRecordDecl())
416 return D->getDefinition();
417 return nullptr;
418}
419
420static TypeInfo getTypeInfoForType(const QualType &T,
421 const PrintingPolicy &Policy) {
422 const TagDecl *TD = getTagDeclForType(T);
423 if (!TD) {
424 TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));
425 TI.IsBuiltIn = T->isBuiltinType();
426 TI.IsTemplate = T->isTemplateTypeParmType();
427 return TI;
428 }
429 InfoType IT;
430 if (isa<EnumDecl>(Val: TD)) {
431 IT = InfoType::IT_enum;
432 } else if (isa<RecordDecl>(Val: TD)) {
433 IT = InfoType::IT_record;
434 } else {
435 IT = InfoType::IT_default;
436 }
437 Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
438 T.getAsString(Policy), getInfoRelativePath(TD));
439 TypeInfo TI = TypeInfo(R);
440 TI.IsBuiltIn = T->isBuiltinType();
441 TI.IsTemplate = T->isTemplateTypeParmType();
442 return TI;
443}
444
445static bool isPublic(const clang::AccessSpecifier AS,
446 const clang::Linkage Link) {
447 if (AS == clang::AccessSpecifier::AS_private)
448 return false;
449 if ((Link == clang::Linkage::Module) || (Link == clang::Linkage::External))
450 return true;
451 return false; // otherwise, linkage is some form of internal linkage
452}
453
454static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
455 const NamedDecl *D) {
456 bool IsAnonymousNamespace = false;
457 if (const auto *N = dyn_cast<NamespaceDecl>(Val: D))
458 IsAnonymousNamespace = N->isAnonymousNamespace();
459 return !PublicOnly ||
460 (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
461 isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
462}
463
464// The InsertChild functions insert the given info into the given scope using
465// the method appropriate for that type. Some types are moved into the
466// appropriate vector, while other types have Reference objects generated to
467// refer to them.
468//
469// See MakeAndInsertIntoParent().
470static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
471 Scope.Namespaces.emplace_back(args: Info.USR, args: Info.Name, args: InfoType::IT_namespace,
472 args: Info.Name, args: getInfoRelativePath(Namespaces: Info.Namespace));
473}
474
475static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
476 Scope.Records.emplace_back(args: Info.USR, args: Info.Name, args: InfoType::IT_record,
477 args: Info.Name, args: getInfoRelativePath(Namespaces: Info.Namespace));
478}
479
480static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
481 Scope.Enums.push_back(x: std::move(Info));
482}
483
484static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
485 Scope.Functions.push_back(x: std::move(Info));
486}
487
488static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
489 Scope.Typedefs.push_back(x: std::move(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>
507static std::unique_ptr<Info> makeAndInsertIntoParent(ChildType Child) {
508 if (Child.Namespace.empty()) {
509 // Insert into unnamed parent namespace.
510 auto ParentNS = std::make_unique<NamespaceInfo>();
511 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
512 return ParentNS;
513 }
514
515 switch (Child.Namespace[0].RefType) {
516 case InfoType::IT_namespace: {
517 auto ParentNS = std::make_unique<NamespaceInfo>();
518 ParentNS->USR = Child.Namespace[0].USR;
519 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
520 return ParentNS;
521 }
522 case InfoType::IT_record: {
523 auto ParentRec = std::make_unique<RecordInfo>();
524 ParentRec->USR = Child.Namespace[0].USR;
525 InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
526 return ParentRec;
527 }
528 default:
529 llvm_unreachable("Invalid reference type for parent namespace");
530 }
531}
532
533// There are two uses for this function.
534// 1) Getting the resulting mode of inheritance of a record.
535// Example: class A {}; class B : private A {}; class C : public B {};
536// It's explicit that C is publicly inherited from C and B is privately
537// inherited from A. It's not explicit but C is also privately inherited from
538// A. This is the AS that this function calculates. FirstAS is the
539// inheritance mode of `class C : B` and SecondAS is the inheritance mode of
540// `class B : A`.
541// 2) Getting the inheritance mode of an inherited attribute / method.
542// Example : class A { public: int M; }; class B : private A {};
543// Class B is inherited from class A, which has a public attribute. This
544// attribute is now part of the derived class B but it's not public. This
545// will be private because the inheritance is private. This is the AS that
546// this function calculates. FirstAS is the inheritance mode and SecondAS is
547// the AS of the attribute / method.
548static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
549 AccessSpecifier SecondAS) {
550 if (FirstAS == AccessSpecifier::AS_none ||
551 SecondAS == AccessSpecifier::AS_none)
552 return AccessSpecifier::AS_none;
553 if (FirstAS == AccessSpecifier::AS_private ||
554 SecondAS == AccessSpecifier::AS_private)
555 return AccessSpecifier::AS_private;
556 if (FirstAS == AccessSpecifier::AS_protected ||
557 SecondAS == AccessSpecifier::AS_protected)
558 return AccessSpecifier::AS_protected;
559 return AccessSpecifier::AS_public;
560}
561
562// The Access parameter is only provided when parsing the field of an inherited
563// record, the access specification of the field depends on the inheritance mode
564static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
565 AccessSpecifier Access = AccessSpecifier::AS_public) {
566 for (const FieldDecl *F : D->fields()) {
567 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
568 continue;
569 populateMemberTypeInfo(I, Access, F);
570 }
571 const auto *CxxRD = dyn_cast<CXXRecordDecl>(Val: D);
572 if (!CxxRD)
573 return;
574 for (Decl *CxxDecl : CxxRD->decls()) {
575 auto *VD = dyn_cast<VarDecl>(CxxDecl);
576 if (!VD ||
577 !shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, VD))
578 continue;
579
580 if (VD->isStaticDataMember())
581 populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true);
582 }
583}
584
585static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
586 for (const EnumConstantDecl *E : D->enumerators()) {
587 std::string ValueExpr;
588 if (const Expr *InitExpr = E->getInitExpr())
589 ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
590 SmallString<16> ValueStr;
591 E->getInitVal().toString(Str&: ValueStr);
592 I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
593 ASTContext &Context = E->getASTContext();
594 if (RawComment *Comment =
595 E->getASTContext().getRawCommentForDeclNoCache(E)) {
596 Comment->setAttached();
597 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
598 EnumValueInfo &Member = I.Members.back();
599 Member.Description.emplace_back();
600 parseFullComment(C: Fc, CI&: Member.Description.back());
601 }
602 }
603 }
604}
605
606static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
607 auto &LO = D->getLangOpts();
608 for (const ParmVarDecl *P : D->parameters()) {
609 FieldTypeInfo &FieldInfo = I.Params.emplace_back(
610 getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());
611 FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
612 }
613}
614
615// TODO: Remove the serialization of Parents and VirtualParents, this
616// information is also extracted in the other definition of parseBases.
617static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
618 // Don't parse bases if this isn't a definition.
619 if (!D->isThisDeclarationADefinition())
620 return;
621
622 for (const CXXBaseSpecifier &B : D->bases()) {
623 if (B.isVirtual())
624 continue;
625 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
626 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
627 I.Parents.emplace_back(Args: getUSRForDecl(D), Args: B.getType().getAsString(),
628 Args: InfoType::IT_record, Args: B.getType().getAsString());
629 } else if (const RecordDecl *P = getRecordDeclForType(T: B.getType()))
630 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
631 InfoType::IT_record, P->getQualifiedNameAsString(),
632 getInfoRelativePath(P));
633 else
634 I.Parents.emplace_back(Args: SymbolID(), Args: B.getType().getAsString());
635 }
636 for (const CXXBaseSpecifier &B : D->vbases()) {
637 if (const RecordDecl *P = getRecordDeclForType(T: B.getType()))
638 I.VirtualParents.emplace_back(
639 getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
640 P->getQualifiedNameAsString(), getInfoRelativePath(P));
641 else
642 I.VirtualParents.emplace_back(Args: SymbolID(), Args: B.getType().getAsString());
643 }
644}
645
646template <typename T>
647static void
648populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
649 const T *D, bool &IsInAnonymousNamespace) {
650 const DeclContext *DC = D->getDeclContext();
651 do {
652 if (const auto *N = dyn_cast<NamespaceDecl>(Val: DC)) {
653 std::string Namespace;
654 if (N->isAnonymousNamespace()) {
655 Namespace = "@nonymous_namespace";
656 IsInAnonymousNamespace = true;
657 } else
658 Namespace = N->getNameAsString();
659 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
660 InfoType::IT_namespace,
661 N->getQualifiedNameAsString());
662 } else if (const auto *N = dyn_cast<RecordDecl>(Val: DC))
663 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
664 InfoType::IT_record,
665 N->getQualifiedNameAsString());
666 else if (const auto *N = dyn_cast<FunctionDecl>(Val: DC))
667 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
668 InfoType::IT_function,
669 N->getQualifiedNameAsString());
670 else if (const auto *N = dyn_cast<EnumDecl>(Val: DC))
671 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
672 InfoType::IT_enum, N->getQualifiedNameAsString());
673 } while ((DC = DC->getParent()));
674 // The global namespace should be added to the list of namespaces if the decl
675 // corresponds to a Record and if it doesn't have any namespace (because this
676 // means it's in the global namespace). Also if its outermost namespace is a
677 // record because that record matches the previous condition mentioned.
678 if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
679 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
680 Namespaces.emplace_back(Args: SymbolID(), Args: "GlobalNamespace",
681 Args: InfoType::IT_namespace);
682}
683
684static void
685populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
686 const clang::Decl *D) {
687 if (const TemplateParameterList *ParamList =
688 D->getDescribedTemplateParams()) {
689 if (!TemplateInfo) {
690 TemplateInfo.emplace();
691 }
692 for (const NamedDecl *ND : *ParamList) {
693 TemplateInfo->Params.emplace_back(
694 getSourceCode(ND, ND->getSourceRange()));
695 }
696 }
697}
698
699static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,
700 const TemplateArgument &Arg) {
701 // The TemplateArgument's pretty printing handles all the normal cases
702 // well enough for our requirements.
703 std::string Str;
704 llvm::raw_string_ostream Stream(Str);
705 Arg.print(Policy: PrintingPolicy(D->getLangOpts()), Out&: Stream, IncludeType: false);
706 return TemplateParamInfo(Str);
707}
708
709template <typename T>
710static void populateInfo(Info &I, const T *D, const FullComment *C,
711 bool &IsInAnonymousNamespace) {
712 I.USR = getUSRForDecl(D);
713 if (auto ConversionDecl = dyn_cast_or_null<CXXConversionDecl>(D);
714 ConversionDecl && ConversionDecl->getConversionType()
715 .getTypePtr()
716 ->isTemplateTypeParmType())
717 I.Name = "operator " + ConversionDecl->getConversionType().getAsString();
718 else
719 I.Name = D->getNameAsString();
720 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
721 if (C) {
722 I.Description.emplace_back();
723 parseFullComment(C, CI&: I.Description.back());
724 }
725}
726
727template <typename T>
728static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
729 Location Loc, bool &IsInAnonymousNamespace) {
730 populateInfo(I, D, C, IsInAnonymousNamespace);
731 if (D->isThisDeclarationADefinition())
732 I.DefLoc = Loc;
733 else
734 I.Loc.emplace_back(Args&: Loc);
735}
736
737static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
738 const FullComment *FC, Location Loc,
739 bool &IsInAnonymousNamespace) {
740 populateSymbolInfo(I, D, C: FC, Loc, IsInAnonymousNamespace);
741 auto &LO = D->getLangOpts();
742 I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
743 I.Prototype = getFunctionPrototype(FuncDecl: D);
744 parseParameters(I, D);
745 I.IsStatic = D->isStatic();
746
747 populateTemplateParameters(I.Template, D);
748
749 // Handle function template specializations.
750 if (const FunctionTemplateSpecializationInfo *FTSI =
751 D->getTemplateSpecializationInfo()) {
752 if (!I.Template)
753 I.Template.emplace();
754 I.Template->Specialization.emplace();
755 auto &Specialization = *I.Template->Specialization;
756
757 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
758
759 // Template parameters to the specialization.
760 if (FTSI->TemplateArguments) {
761 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
762 Specialization.Params.push_back(x: convertTemplateArgToInfo(D, Arg));
763 }
764 }
765 }
766}
767
768static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) {
769 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
770
771 ASTContext &Context = D->getASTContext();
772 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
773 // of this logic. See also similar code in Mapper.cpp.
774 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
775 if (!Comment)
776 return;
777
778 Comment->setAttached();
779 if (comments::FullComment *Fc = Comment->parse(Context, PP: nullptr, D)) {
780 I.Description.emplace_back();
781 parseFullComment(C: Fc, CI&: I.Description.back());
782 }
783}
784
785static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
786 const DeclaratorDecl *D, bool IsStatic) {
787 // Use getAccessUnsafe so that we just get the default AS_none if it's not
788 // valid, as opposed to an assert.
789 MemberTypeInfo &NewMember = I.Members.emplace_back(
790 getTypeInfoForType(D->getTypeSourceInfo()->getType(), D->getLangOpts()),
791 D->getNameAsString(),
792 getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic);
793 populateMemberTypeInfo(NewMember, D);
794}
795
796static void
797parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
798 bool PublicOnly, bool IsParent,
799 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
800 // Don't parse bases if this isn't a definition.
801 if (!D->isThisDeclarationADefinition())
802 return;
803 for (const CXXBaseSpecifier &B : D->bases()) {
804 if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
805 if (const CXXRecordDecl *Base =
806 cast_or_null<CXXRecordDecl>(Val: Ty->getDecl()->getDefinition())) {
807 // Initialized without USR and name, this will be set in the following
808 // if-else stmt.
809 BaseRecordInfo BI(
810 {}, "", getInfoRelativePath(Base), B.isVirtual(),
811 getFinalAccessSpecifier(FirstAS: ParentAccess, SecondAS: B.getAccessSpecifier()),
812 IsParent);
813 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
814 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
815 BI.USR = getUSRForDecl(D);
816 BI.Name = B.getType().getAsString();
817 } else {
818 BI.USR = getUSRForDecl(Base);
819 BI.Name = Base->getNameAsString();
820 }
821 parseFields(BI, Base, PublicOnly, BI.Access);
822 for (const auto &Decl : Base->decls())
823 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
824 // Don't serialize private methods
825 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
826 !MD->isUserProvided())
827 continue;
828 FunctionInfo FI;
829 FI.IsMethod = true;
830 FI.IsStatic = MD->isStatic();
831 // The seventh arg in populateFunctionInfo is a boolean passed by
832 // reference, its value is not relevant in here so it's not used
833 // anywhere besides the function call.
834 bool IsInAnonymousNamespace;
835 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},
836 IsInAnonymousNamespace);
837 FI.Access =
838 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
839 BI.Children.Functions.emplace_back(std::move(FI));
840 }
841 I.Bases.emplace_back(args: std::move(BI));
842 // Call this function recursively to get the inherited classes of
843 // this base; these new bases will also get stored in the original
844 // RecordInfo: I.
845 parseBases(I, D: Base, IsFileInRootDir, PublicOnly, IsParent: false,
846 ParentAccess: I.Bases.back().Access);
847 }
848 }
849 }
850}
851
852std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
853emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
854 bool PublicOnly) {
855 auto NSI = std::make_unique<NamespaceInfo>();
856 bool IsInAnonymousNamespace = false;
857 populateInfo(I&: *NSI, D, C: FC, IsInAnonymousNamespace);
858 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
859 return {};
860
861 NSI->Name = D->isAnonymousNamespace()
862 ? llvm::SmallString<16>("@nonymous_namespace")
863 : NSI->Name;
864 NSI->Path = getInfoRelativePath(Namespaces: NSI->Namespace);
865 if (NSI->Namespace.empty() && NSI->USR == SymbolID())
866 return {std::unique_ptr<Info>{std::move(NSI)}, nullptr};
867
868 // Namespaces are inserted into the parent by reference, so we need to return
869 // both the parent and the record itself.
870 return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(Child: *NSI)};
871}
872
873std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
874emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
875 bool PublicOnly) {
876
877 auto RI = std::make_unique<RecordInfo>();
878 bool IsInAnonymousNamespace = false;
879
880 populateSymbolInfo(I&: *RI, D, C: FC, Loc, IsInAnonymousNamespace);
881 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
882 return {};
883
884 RI->TagType = D->getTagKind();
885 parseFields(I&: *RI, D, PublicOnly);
886
887 if (const auto *C = dyn_cast<CXXRecordDecl>(Val: D)) {
888 RI->FullName = getRecordPrototype(CXXRD: C);
889 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
890 RI->Name = TD->getNameAsString();
891 RI->IsTypeDef = true;
892 }
893 // TODO: remove first call to parseBases, that function should be deleted
894 parseBases(I&: *RI, D: C);
895 parseBases(I&: *RI, D: C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true);
896 }
897 RI->Path = getInfoRelativePath(Namespaces: RI->Namespace);
898
899 populateTemplateParameters(RI->Template, D);
900
901 // Full and partial specializations.
902 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(Val: D)) {
903 if (!RI->Template)
904 RI->Template.emplace();
905 RI->Template->Specialization.emplace();
906 auto &Specialization = *RI->Template->Specialization;
907
908 // What this is a specialization of.
909 auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
910 if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(Val&: SpecOf))
911 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
912 else if (auto *SpecTD =
913 dyn_cast<ClassTemplatePartialSpecializationDecl *>(Val&: SpecOf))
914 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
915
916 // Parameters to the specialization. For partial specializations, get the
917 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
918 // because the non-explicit template parameters will have generated internal
919 // placeholder names rather than the names the user typed that match the
920 // template parameters.
921 if (const ClassTemplatePartialSpecializationDecl *CTPSD =
922 dyn_cast<ClassTemplatePartialSpecializationDecl>(Val: D)) {
923 if (const ASTTemplateArgumentListInfo *AsWritten =
924 CTPSD->getTemplateArgsAsWritten()) {
925 for (unsigned Idx = 0; Idx < AsWritten->getNumTemplateArgs(); Idx++) {
926 Specialization.Params.emplace_back(
927 args: getSourceCode(D, (*AsWritten)[Idx].getSourceRange()));
928 }
929 }
930 } else {
931 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
932 Specialization.Params.push_back(x: convertTemplateArgToInfo(D, Arg));
933 }
934 }
935 }
936
937 // Records are inserted into the parent by reference, so we need to return
938 // both the parent and the record itself.
939 auto Parent = makeAndInsertIntoParent<const RecordInfo &>(Child: *RI);
940 return {std::move(RI), std::move(Parent)};
941}
942
943std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
944emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc,
945 bool PublicOnly) {
946 FunctionInfo Func;
947 bool IsInAnonymousNamespace = false;
948 populateFunctionInfo(I&: Func, D, FC, Loc, IsInAnonymousNamespace);
949 Func.Access = clang::AccessSpecifier::AS_none;
950 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
951 return {};
952
953 // Info is wrapped in its parent scope so is returned in the second position.
954 return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(Child: std::move(Func))};
955}
956
957std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
958emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
959 bool PublicOnly) {
960 FunctionInfo Func;
961 bool IsInAnonymousNamespace = false;
962 populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
963 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
964 return {};
965
966 Func.IsMethod = true;
967 Func.IsStatic = D->isStatic();
968
969 const NamedDecl *Parent = nullptr;
970 if (const auto *SD =
971 dyn_cast<ClassTemplateSpecializationDecl>(Val: D->getParent()))
972 Parent = SD->getSpecializedTemplate();
973 else
974 Parent = D->getParent();
975
976 SymbolID ParentUSR = getUSRForDecl(Parent);
977 Func.Parent =
978 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
979 Parent->getQualifiedNameAsString()};
980 Func.Access = D->getAccess();
981
982 // Info is wrapped in its parent scope so is returned in the second position.
983 return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(Child: std::move(Func))};
984}
985
986static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
987 assert(D && "Invalid Decl when extracting comment");
988 ASTContext &Context = D->getASTContext();
989 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
990 if (!Comment)
991 return;
992
993 Comment->setAttached();
994 if (comments::FullComment *Fc = Comment->parse(Context, PP: nullptr, D)) {
995 Info.Description.emplace_back();
996 parseFullComment(C: Fc, CI&: Info.Description.back());
997 }
998}
999
1000std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1001emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
1002 bool PublicOnly) {
1003 TypedefInfo Info;
1004 bool IsInAnonymousNamespace = false;
1005 populateInfo(I&: Info, D, C: FC, IsInAnonymousNamespace);
1006
1007 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1008 return {};
1009
1010 Info.DefLoc = Loc;
1011 auto &LO = D->getLangOpts();
1012 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1013
1014 if (Info.Underlying.Type.Name.empty()) {
1015 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
1016 // The record serializer explicitly checks for this syntax and constructs
1017 // a record with that name, so we don't want to emit a duplicate here.
1018 return {};
1019 }
1020 Info.IsUsing = false;
1021 extractCommentFromDecl(D, Info);
1022
1023 // Info is wrapped in its parent scope so is returned in the second position.
1024 return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(Child: std::move(Info))};
1025}
1026
1027// A type alias is a C++ "using" declaration for a type. It gets mapped to a
1028// TypedefInfo with the IsUsing flag set.
1029std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1030emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,
1031 bool PublicOnly) {
1032 TypedefInfo Info;
1033 bool IsInAnonymousNamespace = false;
1034 populateInfo(I&: Info, D, C: FC, IsInAnonymousNamespace);
1035 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1036 return {};
1037
1038 Info.DefLoc = Loc;
1039 const LangOptions &LO = D->getLangOpts();
1040 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1041 Info.TypeDeclaration = getTypeAlias(Alias: D);
1042 Info.IsUsing = true;
1043
1044 extractCommentFromDecl(D, Info);
1045
1046 // Info is wrapped in its parent scope so is returned in the second position.
1047 return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(Child: std::move(Info))};
1048}
1049
1050std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
1051emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,
1052 bool PublicOnly) {
1053 EnumInfo Enum;
1054 bool IsInAnonymousNamespace = false;
1055 populateSymbolInfo(I&: Enum, D, C: FC, Loc, IsInAnonymousNamespace);
1056
1057 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1058 return {};
1059
1060 Enum.Scoped = D->isScoped();
1061 if (D->isFixed()) {
1062 auto Name = D->getIntegerType().getAsString();
1063 Enum.BaseType = TypeInfo(Name, Name);
1064 }
1065 parseEnumerators(I&: Enum, D);
1066
1067 // Info is wrapped in its parent scope so is returned in the second position.
1068 return {nullptr, makeAndInsertIntoParent<EnumInfo &&>(Child: std::move(Enum))};
1069}
1070
1071} // namespace serialize
1072} // namespace doc
1073} // namespace clang
1074

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/clang-doc/Serialize.cpp