xref: /llvm-project/clang-tools-extra/clang-doc/Serialize.cpp (revision b68a952ae1c06b78790eab758574d992159e33b8)
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/Comment.h"
12 #include "clang/Index/USRGeneration.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/ADT/Hashing.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/Support/SHA1.h"
17 
18 using clang::comments::FullComment;
19 
20 namespace clang {
21 namespace doc {
22 namespace serialize {
23 
24 SymbolID hashUSR(llvm::StringRef USR) {
25   return llvm::SHA1::hash(arrayRefFromStringRef(USR));
26 }
27 
28 template <typename T>
29 static void
30 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
31                          const T *D, bool &IsAnonymousNamespace);
32 
33 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
34 
35 // A function to extract the appropriate relative path for a given info's
36 // documentation. The path returned is a composite of the parent namespaces.
37 //
38 // Example: Given the below, the directory path for class C info will be
39 // <root>/A/B
40 //
41 // namespace A {
42 // namespace B {
43 //
44 // class C {};
45 //
46 // }
47 // }
48 llvm::SmallString<128>
49 getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
50   llvm::SmallString<128> Path;
51   for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
52     llvm::sys::path::append(Path, R->Name);
53   return Path;
54 }
55 
56 llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
57   llvm::SmallVector<Reference, 4> Namespaces;
58   // The third arg in populateParentNamespaces is a boolean passed by reference,
59   // its value is not relevant in here so it's not used anywhere besides the
60   // function call
61   bool B = true;
62   populateParentNamespaces(Namespaces, D, B);
63   return getInfoRelativePath(Namespaces);
64 }
65 
66 class ClangDocCommentVisitor
67     : public ConstCommentVisitor<ClangDocCommentVisitor> {
68 public:
69   ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
70 
71   void parseComment(const comments::Comment *C);
72 
73   void visitTextComment(const TextComment *C);
74   void visitInlineCommandComment(const InlineCommandComment *C);
75   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
76   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
77   void visitBlockCommandComment(const BlockCommandComment *C);
78   void visitParamCommandComment(const ParamCommandComment *C);
79   void visitTParamCommandComment(const TParamCommandComment *C);
80   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
81   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
82   void visitVerbatimLineComment(const VerbatimLineComment *C);
83 
84 private:
85   std::string getCommandName(unsigned CommandID) const;
86   bool isWhitespaceOnly(StringRef S) const;
87 
88   CommentInfo &CurrentCI;
89 };
90 
91 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
92   CurrentCI.Kind = C->getCommentKindName();
93   ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
94   for (comments::Comment *Child :
95        llvm::make_range(C->child_begin(), C->child_end())) {
96     CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
97     ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
98     Visitor.parseComment(Child);
99   }
100 }
101 
102 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
103   if (!isWhitespaceOnly(C->getText()))
104     CurrentCI.Text = C->getText();
105 }
106 
107 void ClangDocCommentVisitor::visitInlineCommandComment(
108     const InlineCommandComment *C) {
109   CurrentCI.Name = getCommandName(C->getCommandID());
110   for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
111     CurrentCI.Args.push_back(C->getArgText(I));
112 }
113 
114 void ClangDocCommentVisitor::visitHTMLStartTagComment(
115     const HTMLStartTagComment *C) {
116   CurrentCI.Name = C->getTagName();
117   CurrentCI.SelfClosing = C->isSelfClosing();
118   for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
119     const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
120     CurrentCI.AttrKeys.push_back(Attr.Name);
121     CurrentCI.AttrValues.push_back(Attr.Value);
122   }
123 }
124 
125 void ClangDocCommentVisitor::visitHTMLEndTagComment(
126     const HTMLEndTagComment *C) {
127   CurrentCI.Name = C->getTagName();
128   CurrentCI.SelfClosing = true;
129 }
130 
131 void ClangDocCommentVisitor::visitBlockCommandComment(
132     const BlockCommandComment *C) {
133   CurrentCI.Name = getCommandName(C->getCommandID());
134   for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
135     CurrentCI.Args.push_back(C->getArgText(I));
136 }
137 
138 void ClangDocCommentVisitor::visitParamCommandComment(
139     const ParamCommandComment *C) {
140   CurrentCI.Direction =
141       ParamCommandComment::getDirectionAsString(C->getDirection());
142   CurrentCI.Explicit = C->isDirectionExplicit();
143   if (C->hasParamName())
144     CurrentCI.ParamName = C->getParamNameAsWritten();
145 }
146 
147 void ClangDocCommentVisitor::visitTParamCommandComment(
148     const TParamCommandComment *C) {
149   if (C->hasParamName())
150     CurrentCI.ParamName = C->getParamNameAsWritten();
151 }
152 
153 void ClangDocCommentVisitor::visitVerbatimBlockComment(
154     const VerbatimBlockComment *C) {
155   CurrentCI.Name = getCommandName(C->getCommandID());
156   CurrentCI.CloseName = C->getCloseName();
157 }
158 
159 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
160     const VerbatimBlockLineComment *C) {
161   if (!isWhitespaceOnly(C->getText()))
162     CurrentCI.Text = C->getText();
163 }
164 
165 void ClangDocCommentVisitor::visitVerbatimLineComment(
166     const VerbatimLineComment *C) {
167   if (!isWhitespaceOnly(C->getText()))
168     CurrentCI.Text = C->getText();
169 }
170 
171 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
172   return llvm::all_of(S, isspace);
173 }
174 
175 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
176   const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
177   if (Info)
178     return Info->Name;
179   // TODO: Add parsing for \file command.
180   return "<not a builtin command>";
181 }
182 
183 // Serializing functions.
184 
185 std::string getSourceCode(const Decl *D, const SourceRange &R) {
186   return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
187                               D->getASTContext().getSourceManager(),
188                               D->getASTContext().getLangOpts())
189       .str();
190 }
191 
192 template <typename T> static std::string serialize(T &I) {
193   SmallString<2048> Buffer;
194   llvm::BitstreamWriter Stream(Buffer);
195   ClangDocBitcodeWriter Writer(Stream);
196   Writer.emitBlock(I);
197   return Buffer.str().str();
198 }
199 
200 std::string serialize(std::unique_ptr<Info> &I) {
201   switch (I->IT) {
202   case InfoType::IT_namespace:
203     return serialize(*static_cast<NamespaceInfo *>(I.get()));
204   case InfoType::IT_record:
205     return serialize(*static_cast<RecordInfo *>(I.get()));
206   case InfoType::IT_enum:
207     return serialize(*static_cast<EnumInfo *>(I.get()));
208   case InfoType::IT_function:
209     return serialize(*static_cast<FunctionInfo *>(I.get()));
210   default:
211     return "";
212   }
213 }
214 
215 static void parseFullComment(const FullComment *C, CommentInfo &CI) {
216   ClangDocCommentVisitor Visitor(CI);
217   Visitor.parseComment(C);
218 }
219 
220 static SymbolID getUSRForDecl(const Decl *D) {
221   llvm::SmallString<128> USR;
222   if (index::generateUSRForDecl(D, USR))
223     return SymbolID();
224   return hashUSR(USR);
225 }
226 
227 static TagDecl *getTagDeclForType(const QualType &T) {
228   if (const TagDecl *D = T->getAsTagDecl())
229     return D->getDefinition();
230   return nullptr;
231 }
232 
233 static RecordDecl *getRecordDeclForType(const QualType &T) {
234   if (const RecordDecl *D = T->getAsRecordDecl())
235     return D->getDefinition();
236   return nullptr;
237 }
238 
239 TypeInfo getTypeInfoForType(const QualType &T, const PrintingPolicy &Policy) {
240   const TagDecl *TD = getTagDeclForType(T);
241   if (!TD)
242     return TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));
243 
244   InfoType IT;
245   if (dyn_cast<EnumDecl>(TD)) {
246     IT = InfoType::IT_enum;
247   } else if (dyn_cast<RecordDecl>(TD)) {
248     IT = InfoType::IT_record;
249   } else {
250     IT = InfoType::IT_default;
251   }
252   return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
253                             T.getAsString(Policy), getInfoRelativePath(TD)));
254 }
255 
256 static bool isPublic(const clang::AccessSpecifier AS,
257                      const clang::Linkage Link) {
258   if (AS == clang::AccessSpecifier::AS_private)
259     return false;
260   else if ((Link == clang::Linkage::Module) ||
261            (Link == clang::Linkage::External))
262     return true;
263   return false; // otherwise, linkage is some form of internal linkage
264 }
265 
266 static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
267                                 const NamedDecl *D) {
268   bool IsAnonymousNamespace = false;
269   if (const auto *N = dyn_cast<NamespaceDecl>(D))
270     IsAnonymousNamespace = N->isAnonymousNamespace();
271   return !PublicOnly ||
272          (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
273           isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
274 }
275 
276 // The InsertChild functions insert the given info into the given scope using
277 // the method appropriate for that type. Some types are moved into the
278 // appropriate vector, while other types have Reference objects generated to
279 // refer to them.
280 //
281 // See MakeAndInsertIntoParent().
282 static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
283   Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
284                                 Info.Name, getInfoRelativePath(Info.Namespace));
285 }
286 
287 static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
288   Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
289                              Info.Name, getInfoRelativePath(Info.Namespace));
290 }
291 
292 static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
293   Scope.Enums.push_back(std::move(Info));
294 }
295 
296 static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
297   Scope.Functions.push_back(std::move(Info));
298 }
299 
300 static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
301   Scope.Typedefs.push_back(std::move(Info));
302 }
303 
304 // Creates a parent of the correct type for the given child and inserts it into
305 // that parent.
306 //
307 // This is complicated by the fact that namespaces and records are inserted by
308 // reference (constructing a "Reference" object with that namespace/record's
309 // info), while everything else is inserted by moving it directly into the child
310 // vectors.
311 //
312 // For namespaces and records, explicitly specify a const& template parameter
313 // when invoking this function:
314 //   MakeAndInsertIntoParent<const Record&>(...);
315 // Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
316 // parameter. Since each variant is used once, it's not worth having a more
317 // elaborate system to automatically deduce this information.
318 template <typename ChildType>
319 std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
320   if (Child.Namespace.empty()) {
321     // Insert into unnamed parent namespace.
322     auto ParentNS = std::make_unique<NamespaceInfo>();
323     InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
324     return ParentNS;
325   }
326 
327   switch (Child.Namespace[0].RefType) {
328   case InfoType::IT_namespace: {
329     auto ParentNS = std::make_unique<NamespaceInfo>();
330     ParentNS->USR = Child.Namespace[0].USR;
331     InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
332     return ParentNS;
333   }
334   case InfoType::IT_record: {
335     auto ParentRec = std::make_unique<RecordInfo>();
336     ParentRec->USR = Child.Namespace[0].USR;
337     InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
338     return ParentRec;
339   }
340   default:
341     llvm_unreachable("Invalid reference type for parent namespace");
342   }
343 }
344 
345 // There are two uses for this function.
346 // 1) Getting the resulting mode of inheritance of a record.
347 //    Example: class A {}; class B : private A {}; class C : public B {};
348 //    It's explicit that C is publicly inherited from C and B is privately
349 //    inherited from A. It's not explicit but C is also privately inherited from
350 //    A. This is the AS that this function calculates. FirstAS is the
351 //    inheritance mode of `class C : B` and SecondAS is the inheritance mode of
352 //    `class B : A`.
353 // 2) Getting the inheritance mode of an inherited attribute / method.
354 //    Example : class A { public: int M; }; class B : private A {};
355 //    Class B is inherited from class A, which has a public attribute. This
356 //    attribute is now part of the derived class B but it's not public. This
357 //    will be private because the inheritance is private. This is the AS that
358 //    this function calculates. FirstAS is the inheritance mode and SecondAS is
359 //    the AS of the attribute / method.
360 static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
361                                                AccessSpecifier SecondAS) {
362   if (FirstAS == AccessSpecifier::AS_none ||
363       SecondAS == AccessSpecifier::AS_none)
364     return AccessSpecifier::AS_none;
365   if (FirstAS == AccessSpecifier::AS_private ||
366       SecondAS == AccessSpecifier::AS_private)
367     return AccessSpecifier::AS_private;
368   if (FirstAS == AccessSpecifier::AS_protected ||
369       SecondAS == AccessSpecifier::AS_protected)
370     return AccessSpecifier::AS_protected;
371   return AccessSpecifier::AS_public;
372 }
373 
374 // The Access parameter is only provided when parsing the field of an inherited
375 // record, the access specification of the field depends on the inheritance mode
376 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
377                         AccessSpecifier Access = AccessSpecifier::AS_public) {
378   for (const FieldDecl *F : D->fields()) {
379     if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
380       continue;
381 
382     auto &LO = F->getLangOpts();
383     // Use getAccessUnsafe so that we just get the default AS_none if it's not
384     // valid, as opposed to an assert.
385     MemberTypeInfo &NewMember = I.Members.emplace_back(
386         getTypeInfoForType(F->getTypeSourceInfo()->getType(), LO),
387         F->getNameAsString(),
388         getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
389     populateMemberTypeInfo(NewMember, F);
390   }
391 }
392 
393 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
394   for (const EnumConstantDecl *E : D->enumerators()) {
395     std::string ValueExpr;
396     if (const Expr *InitExpr = E->getInitExpr())
397       ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
398     SmallString<16> ValueStr;
399     E->getInitVal().toString(ValueStr);
400     I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
401     ASTContext &Context = E->getASTContext();
402     if (RawComment *Comment =
403             E->getASTContext().getRawCommentForDeclNoCache(E)) {
404       CommentInfo CInfo;
405       Comment->setAttached();
406       if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
407         EnumValueInfo &Member = I.Members.back();
408         Member.Description.emplace_back();
409         parseFullComment(Fc, Member.Description.back());
410       }
411     }
412   }
413 }
414 
415 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
416   auto &LO = D->getLangOpts();
417   for (const ParmVarDecl *P : D->parameters()) {
418     FieldTypeInfo &FieldInfo = I.Params.emplace_back(
419         getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());
420     FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
421   }
422 }
423 
424 // TODO: Remove the serialization of Parents and VirtualParents, this
425 // information is also extracted in the other definition of parseBases.
426 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
427   // Don't parse bases if this isn't a definition.
428   if (!D->isThisDeclarationADefinition())
429     return;
430   for (const CXXBaseSpecifier &B : D->bases()) {
431     if (B.isVirtual())
432       continue;
433     if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
434       const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
435       I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
436                              InfoType::IT_record, B.getType().getAsString());
437     } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
438       I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
439                              InfoType::IT_record, P->getQualifiedNameAsString(),
440                              getInfoRelativePath(P));
441     else
442       I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
443   }
444   for (const CXXBaseSpecifier &B : D->vbases()) {
445     if (const RecordDecl *P = getRecordDeclForType(B.getType()))
446       I.VirtualParents.emplace_back(
447           getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
448           P->getQualifiedNameAsString(), getInfoRelativePath(P));
449     else
450       I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
451   }
452 }
453 
454 template <typename T>
455 static void
456 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
457                          const T *D, bool &IsInAnonymousNamespace) {
458   const DeclContext *DC = D->getDeclContext();
459   do {
460     if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
461       std::string Namespace;
462       if (N->isAnonymousNamespace()) {
463         Namespace = "@nonymous_namespace";
464         IsInAnonymousNamespace = true;
465       } else
466         Namespace = N->getNameAsString();
467       Namespaces.emplace_back(getUSRForDecl(N), Namespace,
468                               InfoType::IT_namespace,
469                               N->getQualifiedNameAsString());
470     } else if (const auto *N = dyn_cast<RecordDecl>(DC))
471       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
472                               InfoType::IT_record,
473                               N->getQualifiedNameAsString());
474     else if (const auto *N = dyn_cast<FunctionDecl>(DC))
475       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
476                               InfoType::IT_function,
477                               N->getQualifiedNameAsString());
478     else if (const auto *N = dyn_cast<EnumDecl>(DC))
479       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
480                               InfoType::IT_enum, N->getQualifiedNameAsString());
481   } while ((DC = DC->getParent()));
482   // The global namespace should be added to the list of namespaces if the decl
483   // corresponds to a Record and if it doesn't have any namespace (because this
484   // means it's in the global namespace). Also if its outermost namespace is a
485   // record because that record matches the previous condition mentioned.
486   if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
487       (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
488     Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
489                             InfoType::IT_namespace);
490 }
491 
492 void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
493                                 const clang::Decl *D) {
494   if (const TemplateParameterList *ParamList =
495           D->getDescribedTemplateParams()) {
496     if (!TemplateInfo) {
497       TemplateInfo.emplace();
498     }
499     for (const NamedDecl *ND : *ParamList) {
500       TemplateInfo->Params.emplace_back(
501           getSourceCode(ND, ND->getSourceRange()));
502     }
503   }
504 }
505 
506 TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
507                                          const TemplateArgument &Arg) {
508   // The TemplateArgument's pretty printing handles all the normal cases
509   // well enough for our requirements.
510   std::string Str;
511   llvm::raw_string_ostream Stream(Str);
512   Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
513   return TemplateParamInfo(Str);
514 }
515 
516 template <typename T>
517 static void populateInfo(Info &I, const T *D, const FullComment *C,
518                          bool &IsInAnonymousNamespace) {
519   I.USR = getUSRForDecl(D);
520   I.Name = D->getNameAsString();
521   populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
522   if (C) {
523     I.Description.emplace_back();
524     parseFullComment(C, I.Description.back());
525   }
526 }
527 
528 template <typename T>
529 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
530                                int LineNumber, StringRef Filename,
531                                bool IsFileInRootDir,
532                                bool &IsInAnonymousNamespace) {
533   populateInfo(I, D, C, IsInAnonymousNamespace);
534   if (D->isThisDeclarationADefinition())
535     I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
536   else
537     I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
538 }
539 
540 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
541                                  const FullComment *FC, int LineNumber,
542                                  StringRef Filename, bool IsFileInRootDir,
543                                  bool &IsInAnonymousNamespace) {
544   populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
545                      IsInAnonymousNamespace);
546   auto &LO = D->getLangOpts();
547   I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
548   parseParameters(I, D);
549 
550   PopulateTemplateParameters(I.Template, D);
551 
552   // Handle function template specializations.
553   if (const FunctionTemplateSpecializationInfo *FTSI =
554           D->getTemplateSpecializationInfo()) {
555     if (!I.Template)
556       I.Template.emplace();
557     I.Template->Specialization.emplace();
558     auto &Specialization = *I.Template->Specialization;
559 
560     Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
561 
562     // Template parameters to the specialization.
563     if (FTSI->TemplateArguments) {
564       for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
565         Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
566       }
567     }
568   }
569 }
570 
571 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
572   assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
573 
574   ASTContext& Context = D->getASTContext();
575   // TODO investigate whether we can use ASTContext::getCommentForDecl instead
576   // of this logic. See also similar code in Mapper.cpp.
577   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
578   if (!Comment)
579     return;
580 
581   Comment->setAttached();
582   if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
583     I.Description.emplace_back();
584     parseFullComment(fc, I.Description.back());
585   }
586 }
587 
588 static void
589 parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
590            bool PublicOnly, bool IsParent,
591            AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
592   // Don't parse bases if this isn't a definition.
593   if (!D->isThisDeclarationADefinition())
594     return;
595   for (const CXXBaseSpecifier &B : D->bases()) {
596     if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
597       if (const CXXRecordDecl *Base =
598               cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
599         // Initialized without USR and name, this will be set in the following
600         // if-else stmt.
601         BaseRecordInfo BI(
602             {}, "", getInfoRelativePath(Base), B.isVirtual(),
603             getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
604             IsParent);
605         if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
606           const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
607           BI.USR = getUSRForDecl(D);
608           BI.Name = B.getType().getAsString();
609         } else {
610           BI.USR = getUSRForDecl(Base);
611           BI.Name = Base->getNameAsString();
612         }
613         parseFields(BI, Base, PublicOnly, BI.Access);
614         for (const auto &Decl : Base->decls())
615           if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
616             // Don't serialize private methods
617             if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
618                 !MD->isUserProvided())
619               continue;
620             FunctionInfo FI;
621             FI.IsMethod = true;
622             // The seventh arg in populateFunctionInfo is a boolean passed by
623             // reference, its value is not relevant in here so it's not used
624             // anywhere besides the function call.
625             bool IsInAnonymousNamespace;
626             populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
627                                  /*FileName=*/{}, IsFileInRootDir,
628                                  IsInAnonymousNamespace);
629             FI.Access =
630                 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
631             BI.Children.Functions.emplace_back(std::move(FI));
632           }
633         I.Bases.emplace_back(std::move(BI));
634         // Call this function recursively to get the inherited classes of
635         // this base; these new bases will also get stored in the original
636         // RecordInfo: I.
637         parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
638                    I.Bases.back().Access);
639       }
640     }
641   }
642 }
643 
644 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
645 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
646          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
647   auto I = std::make_unique<NamespaceInfo>();
648   bool IsInAnonymousNamespace = false;
649   populateInfo(*I, D, FC, IsInAnonymousNamespace);
650   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
651     return {};
652 
653   I->Name = D->isAnonymousNamespace()
654                 ? llvm::SmallString<16>("@nonymous_namespace")
655                 : I->Name;
656   I->Path = getInfoRelativePath(I->Namespace);
657   if (I->Namespace.empty() && I->USR == SymbolID())
658     return {std::unique_ptr<Info>{std::move(I)}, nullptr};
659 
660   // Namespaces are inserted into the parent by reference, so we need to return
661   // both the parent and the record itself.
662   return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
663 }
664 
665 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
666 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
667          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
668   auto I = std::make_unique<RecordInfo>();
669   bool IsInAnonymousNamespace = false;
670   populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
671                      IsInAnonymousNamespace);
672   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
673     return {};
674 
675   I->TagType = D->getTagKind();
676   parseFields(*I, D, PublicOnly);
677   if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
678     if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
679       I->Name = TD->getNameAsString();
680       I->IsTypeDef = true;
681     }
682     // TODO: remove first call to parseBases, that function should be deleted
683     parseBases(*I, C);
684     parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
685   }
686   I->Path = getInfoRelativePath(I->Namespace);
687 
688   PopulateTemplateParameters(I->Template, D);
689 
690   // Full and partial specializations.
691   if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
692     if (!I->Template)
693       I->Template.emplace();
694     I->Template->Specialization.emplace();
695     auto &Specialization = *I->Template->Specialization;
696 
697     // What this is a specialization of.
698     auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
699     if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
700       Specialization.SpecializationOf = getUSRForDecl(CTD);
701     else if (auto *CTPSD =
702                  dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
703       Specialization.SpecializationOf = getUSRForDecl(CTPSD);
704 
705     // Parameters to the specilization. For partial specializations, get the
706     // parameters "as written" from the ClassTemplatePartialSpecializationDecl
707     // because the non-explicit template parameters will have generated internal
708     // placeholder names rather than the names the user typed that match the
709     // template parameters.
710     if (const ClassTemplatePartialSpecializationDecl *CTPSD =
711             dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
712       if (const ASTTemplateArgumentListInfo *AsWritten =
713               CTPSD->getTemplateArgsAsWritten()) {
714         for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
715           Specialization.Params.emplace_back(
716               getSourceCode(D, (*AsWritten)[i].getSourceRange()));
717         }
718       }
719     } else {
720       for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
721         Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
722       }
723     }
724   }
725 
726   // Records are inserted into the parent by reference, so we need to return
727   // both the parent and the record itself.
728   auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
729   return {std::move(I), std::move(Parent)};
730 }
731 
732 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
733 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
734          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
735   FunctionInfo Func;
736   bool IsInAnonymousNamespace = false;
737   populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
738                        IsInAnonymousNamespace);
739   Func.Access = clang::AccessSpecifier::AS_none;
740   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
741     return {};
742 
743   // Info is wrapped in its parent scope so is returned in the second position.
744   return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
745 }
746 
747 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
748 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
749          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
750   FunctionInfo Func;
751   bool IsInAnonymousNamespace = false;
752   populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
753                        IsInAnonymousNamespace);
754   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
755     return {};
756 
757   Func.IsMethod = true;
758 
759   const NamedDecl *Parent = nullptr;
760   if (const auto *SD =
761           dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
762     Parent = SD->getSpecializedTemplate();
763   else
764     Parent = D->getParent();
765 
766   SymbolID ParentUSR = getUSRForDecl(Parent);
767   Func.Parent =
768       Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
769                 Parent->getQualifiedNameAsString()};
770   Func.Access = D->getAccess();
771 
772   // Info is wrapped in its parent scope so is returned in the second position.
773   return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
774 }
775 
776 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
777 emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
778          StringRef File, bool IsFileInRootDir, bool PublicOnly) {
779   TypedefInfo Info;
780 
781   bool IsInAnonymousNamespace = false;
782   populateInfo(Info, D, FC, IsInAnonymousNamespace);
783   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
784     return {};
785 
786   Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
787   auto &LO = D->getLangOpts();
788   Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
789   if (Info.Underlying.Type.Name.empty()) {
790     // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
791     // The record serializer explicitly checks for this syntax and constructs
792     // a record with that name, so we don't want to emit a duplicate here.
793     return {};
794   }
795   Info.IsUsing = false;
796 
797   // Info is wrapped in its parent scope so is returned in the second position.
798   return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
799 }
800 
801 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
802 // TypedefInfo with the IsUsing flag set.
803 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
804 emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
805          StringRef File, bool IsFileInRootDir, bool PublicOnly) {
806   TypedefInfo Info;
807 
808   bool IsInAnonymousNamespace = false;
809   populateInfo(Info, D, FC, IsInAnonymousNamespace);
810   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
811     return {};
812 
813   Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
814   auto &LO = D->getLangOpts();
815   Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
816   Info.IsUsing = true;
817 
818   // Info is wrapped in its parent scope so is returned in the second position.
819   return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
820 }
821 
822 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
823 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
824          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
825   EnumInfo Enum;
826   bool IsInAnonymousNamespace = false;
827   populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
828                      IsInAnonymousNamespace);
829   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
830     return {};
831 
832   Enum.Scoped = D->isScoped();
833   if (D->isFixed()) {
834     auto Name = D->getIntegerType().getAsString();
835     Enum.BaseType = TypeInfo(Name, Name);
836   }
837   parseEnumerators(Enum, D);
838 
839   // Info is wrapped in its parent scope so is returned in the second position.
840   return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
841 }
842 
843 } // namespace serialize
844 } // namespace doc
845 } // namespace clang
846