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 "llvm/ADT/Hashing.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/Support/SHA1.h"
16 
17 using clang::comments::FullComment;
18 
19 namespace clang {
20 namespace doc {
21 namespace serialize {
22 
hashUSR(llvm::StringRef USR)23 SymbolID hashUSR(llvm::StringRef USR) {
24   return llvm::SHA1::hash(arrayRefFromStringRef(USR));
25 }
26 
27 template <typename T>
28 static void
29 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
30                          const T *D, bool &IsAnonymousNamespace);
31 
32 // A function to extract the appropriate relative path for a given info's
33 // documentation. The path returned is a composite of the parent namespaces.
34 //
35 // Example: Given the below, the directory path for class C info will be
36 // <root>/A/B
37 //
38 // namespace A {
39 // namespace B {
40 //
41 // class C {};
42 //
43 // }
44 // }
45 llvm::SmallString<128>
getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> & Namespaces)46 getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
47   llvm::SmallString<128> Path;
48   for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
49     llvm::sys::path::append(Path, R->Name);
50   return Path;
51 }
52 
getInfoRelativePath(const Decl * D)53 llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
54   llvm::SmallVector<Reference, 4> Namespaces;
55   // The third arg in populateParentNamespaces is a boolean passed by reference,
56   // its value is not relevant in here so it's not used anywhere besides the
57   // function call
58   bool B = true;
59   populateParentNamespaces(Namespaces, D, B);
60   return getInfoRelativePath(Namespaces);
61 }
62 
63 class ClangDocCommentVisitor
64     : public ConstCommentVisitor<ClangDocCommentVisitor> {
65 public:
ClangDocCommentVisitor(CommentInfo & CI)66   ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
67 
68   void parseComment(const comments::Comment *C);
69 
70   void visitTextComment(const TextComment *C);
71   void visitInlineCommandComment(const InlineCommandComment *C);
72   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
73   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
74   void visitBlockCommandComment(const BlockCommandComment *C);
75   void visitParamCommandComment(const ParamCommandComment *C);
76   void visitTParamCommandComment(const TParamCommandComment *C);
77   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
78   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
79   void visitVerbatimLineComment(const VerbatimLineComment *C);
80 
81 private:
82   std::string getCommandName(unsigned CommandID) const;
83   bool isWhitespaceOnly(StringRef S) const;
84 
85   CommentInfo &CurrentCI;
86 };
87 
parseComment(const comments::Comment * C)88 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
89   CurrentCI.Kind = C->getCommentKindName();
90   ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
91   for (comments::Comment *Child :
92        llvm::make_range(C->child_begin(), C->child_end())) {
93     CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
94     ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
95     Visitor.parseComment(Child);
96   }
97 }
98 
visitTextComment(const TextComment * C)99 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
100   if (!isWhitespaceOnly(C->getText()))
101     CurrentCI.Text = C->getText();
102 }
103 
visitInlineCommandComment(const InlineCommandComment * C)104 void ClangDocCommentVisitor::visitInlineCommandComment(
105     const InlineCommandComment *C) {
106   CurrentCI.Name = getCommandName(C->getCommandID());
107   for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
108     CurrentCI.Args.push_back(C->getArgText(I));
109 }
110 
visitHTMLStartTagComment(const HTMLStartTagComment * C)111 void ClangDocCommentVisitor::visitHTMLStartTagComment(
112     const HTMLStartTagComment *C) {
113   CurrentCI.Name = C->getTagName();
114   CurrentCI.SelfClosing = C->isSelfClosing();
115   for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
116     const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
117     CurrentCI.AttrKeys.push_back(Attr.Name);
118     CurrentCI.AttrValues.push_back(Attr.Value);
119   }
120 }
121 
visitHTMLEndTagComment(const HTMLEndTagComment * C)122 void ClangDocCommentVisitor::visitHTMLEndTagComment(
123     const HTMLEndTagComment *C) {
124   CurrentCI.Name = C->getTagName();
125   CurrentCI.SelfClosing = true;
126 }
127 
visitBlockCommandComment(const BlockCommandComment * C)128 void ClangDocCommentVisitor::visitBlockCommandComment(
129     const BlockCommandComment *C) {
130   CurrentCI.Name = getCommandName(C->getCommandID());
131   for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
132     CurrentCI.Args.push_back(C->getArgText(I));
133 }
134 
visitParamCommandComment(const ParamCommandComment * C)135 void ClangDocCommentVisitor::visitParamCommandComment(
136     const ParamCommandComment *C) {
137   CurrentCI.Direction =
138       ParamCommandComment::getDirectionAsString(C->getDirection());
139   CurrentCI.Explicit = C->isDirectionExplicit();
140   if (C->hasParamName())
141     CurrentCI.ParamName = C->getParamNameAsWritten();
142 }
143 
visitTParamCommandComment(const TParamCommandComment * C)144 void ClangDocCommentVisitor::visitTParamCommandComment(
145     const TParamCommandComment *C) {
146   if (C->hasParamName())
147     CurrentCI.ParamName = C->getParamNameAsWritten();
148 }
149 
visitVerbatimBlockComment(const VerbatimBlockComment * C)150 void ClangDocCommentVisitor::visitVerbatimBlockComment(
151     const VerbatimBlockComment *C) {
152   CurrentCI.Name = getCommandName(C->getCommandID());
153   CurrentCI.CloseName = C->getCloseName();
154 }
155 
visitVerbatimBlockLineComment(const VerbatimBlockLineComment * C)156 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
157     const VerbatimBlockLineComment *C) {
158   if (!isWhitespaceOnly(C->getText()))
159     CurrentCI.Text = C->getText();
160 }
161 
visitVerbatimLineComment(const VerbatimLineComment * C)162 void ClangDocCommentVisitor::visitVerbatimLineComment(
163     const VerbatimLineComment *C) {
164   if (!isWhitespaceOnly(C->getText()))
165     CurrentCI.Text = C->getText();
166 }
167 
isWhitespaceOnly(llvm::StringRef S) const168 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
169   return std::all_of(S.begin(), S.end(), isspace);
170 }
171 
getCommandName(unsigned CommandID) const172 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
173   const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
174   if (Info)
175     return Info->Name;
176   // TODO: Add parsing for \file command.
177   return "<not a builtin command>";
178 }
179 
180 // Serializing functions.
181 
serialize(T & I)182 template <typename T> static std::string serialize(T &I) {
183   SmallString<2048> Buffer;
184   llvm::BitstreamWriter Stream(Buffer);
185   ClangDocBitcodeWriter Writer(Stream);
186   Writer.emitBlock(I);
187   return Buffer.str().str();
188 }
189 
serialize(std::unique_ptr<Info> & I)190 std::string serialize(std::unique_ptr<Info> &I) {
191   switch (I->IT) {
192   case InfoType::IT_namespace:
193     return serialize(*static_cast<NamespaceInfo *>(I.get()));
194   case InfoType::IT_record:
195     return serialize(*static_cast<RecordInfo *>(I.get()));
196   case InfoType::IT_enum:
197     return serialize(*static_cast<EnumInfo *>(I.get()));
198   case InfoType::IT_function:
199     return serialize(*static_cast<FunctionInfo *>(I.get()));
200   default:
201     return "";
202   }
203 }
204 
parseFullComment(const FullComment * C,CommentInfo & CI)205 static void parseFullComment(const FullComment *C, CommentInfo &CI) {
206   ClangDocCommentVisitor Visitor(CI);
207   Visitor.parseComment(C);
208 }
209 
getUSRForDecl(const Decl * D)210 static SymbolID getUSRForDecl(const Decl *D) {
211   llvm::SmallString<128> USR;
212   if (index::generateUSRForDecl(D, USR))
213     return SymbolID();
214   return hashUSR(USR);
215 }
216 
getDeclForType(const QualType & T)217 static RecordDecl *getDeclForType(const QualType &T) {
218   if (const RecordDecl *D = T->getAsRecordDecl())
219     return D->getDefinition();
220   return nullptr;
221 }
222 
isPublic(const clang::AccessSpecifier AS,const clang::Linkage Link)223 static bool isPublic(const clang::AccessSpecifier AS,
224                      const clang::Linkage Link) {
225   if (AS == clang::AccessSpecifier::AS_private)
226     return false;
227   else if ((Link == clang::Linkage::ModuleLinkage) ||
228            (Link == clang::Linkage::ExternalLinkage))
229     return true;
230   return false; // otherwise, linkage is some form of internal linkage
231 }
232 
shouldSerializeInfo(bool PublicOnly,bool IsInAnonymousNamespace,const NamedDecl * D)233 static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
234                                 const NamedDecl *D) {
235   bool IsAnonymousNamespace = false;
236   if (const auto *N = dyn_cast<NamespaceDecl>(D))
237     IsAnonymousNamespace = N->isAnonymousNamespace();
238   return !PublicOnly ||
239          (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
240           isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
241 }
242 
243 // There are two uses for this function.
244 // 1) Getting the resulting mode of inheritance of a record.
245 //    Example: class A {}; class B : private A {}; class C : public B {};
246 //    It's explicit that C is publicly inherited from C and B is privately
247 //    inherited from A. It's not explicit but C is also privately inherited from
248 //    A. This is the AS that this function calculates. FirstAS is the
249 //    inheritance mode of `class C : B` and SecondAS is the inheritance mode of
250 //    `class B : A`.
251 // 2) Getting the inheritance mode of an inherited attribute / method.
252 //    Example : class A { public: int M; }; class B : private A {};
253 //    Class B is inherited from class A, which has a public attribute. This
254 //    attribute is now part of the derived class B but it's not public. This
255 //    will be private because the inheritance is private. This is the AS that
256 //    this function calculates. FirstAS is the inheritance mode and SecondAS is
257 //    the AS of the attribute / method.
getFinalAccessSpecifier(AccessSpecifier FirstAS,AccessSpecifier SecondAS)258 static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
259                                                AccessSpecifier SecondAS) {
260   if (FirstAS == AccessSpecifier::AS_none ||
261       SecondAS == AccessSpecifier::AS_none)
262     return AccessSpecifier::AS_none;
263   if (FirstAS == AccessSpecifier::AS_private ||
264       SecondAS == AccessSpecifier::AS_private)
265     return AccessSpecifier::AS_private;
266   if (FirstAS == AccessSpecifier::AS_protected ||
267       SecondAS == AccessSpecifier::AS_protected)
268     return AccessSpecifier::AS_protected;
269   return AccessSpecifier::AS_public;
270 }
271 
272 // The Access parameter is only provided when parsing the field of an inherited
273 // record, the access specification of the field depends on the inheritance mode
parseFields(RecordInfo & I,const RecordDecl * D,bool PublicOnly,AccessSpecifier Access=AccessSpecifier::AS_public)274 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
275                         AccessSpecifier Access = AccessSpecifier::AS_public) {
276   for (const FieldDecl *F : D->fields()) {
277     if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
278       continue;
279     if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
280       // Use getAccessUnsafe so that we just get the default AS_none if it's not
281       // valid, as opposed to an assert.
282       if (const auto *N = dyn_cast<EnumDecl>(T)) {
283         I.Members.emplace_back(
284             getUSRForDecl(T), N->getNameAsString(), InfoType::IT_enum,
285             getInfoRelativePath(N), F->getNameAsString(),
286             getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
287         continue;
288       } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
289         I.Members.emplace_back(
290             getUSRForDecl(T), N->getNameAsString(), InfoType::IT_record,
291             getInfoRelativePath(N), F->getNameAsString(),
292             getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
293         continue;
294       }
295     }
296     I.Members.emplace_back(
297         F->getTypeSourceInfo()->getType().getAsString(), F->getNameAsString(),
298         getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
299   }
300 }
301 
parseEnumerators(EnumInfo & I,const EnumDecl * D)302 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
303   for (const EnumConstantDecl *E : D->enumerators())
304     I.Members.emplace_back(E->getNameAsString());
305 }
306 
parseParameters(FunctionInfo & I,const FunctionDecl * D)307 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
308   for (const ParmVarDecl *P : D->parameters()) {
309     if (const auto *T = getDeclForType(P->getOriginalType())) {
310       if (const auto *N = dyn_cast<EnumDecl>(T)) {
311         I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
312                               InfoType::IT_enum, getInfoRelativePath(N),
313                               P->getNameAsString());
314         continue;
315       } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
316         I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
317                               InfoType::IT_record, getInfoRelativePath(N),
318                               P->getNameAsString());
319         continue;
320       }
321     }
322     I.Params.emplace_back(P->getOriginalType().getAsString(),
323                           P->getNameAsString());
324   }
325 }
326 
327 // TODO: Remove the serialization of Parents and VirtualParents, this
328 // information is also extracted in the other definition of parseBases.
parseBases(RecordInfo & I,const CXXRecordDecl * D)329 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
330   // Don't parse bases if this isn't a definition.
331   if (!D->isThisDeclarationADefinition())
332     return;
333   for (const CXXBaseSpecifier &B : D->bases()) {
334     if (B.isVirtual())
335       continue;
336     if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
337       const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
338       I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
339                              InfoType::IT_record);
340     } else if (const RecordDecl *P = getDeclForType(B.getType()))
341       I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
342                              InfoType::IT_record, getInfoRelativePath(P));
343     else
344       I.Parents.emplace_back(B.getType().getAsString());
345   }
346   for (const CXXBaseSpecifier &B : D->vbases()) {
347     if (const auto *P = getDeclForType(B.getType()))
348       I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
349                                     InfoType::IT_record,
350                                     getInfoRelativePath(P));
351     else
352       I.VirtualParents.emplace_back(B.getType().getAsString());
353   }
354 }
355 
356 template <typename T>
357 static void
populateParentNamespaces(llvm::SmallVector<Reference,4> & Namespaces,const T * D,bool & IsInAnonymousNamespace)358 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
359                          const T *D, bool &IsInAnonymousNamespace) {
360   const auto *DC = dyn_cast<DeclContext>(D);
361   while ((DC = DC->getParent())) {
362     if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
363       std::string Namespace;
364       if (N->isAnonymousNamespace()) {
365         Namespace = "@nonymous_namespace";
366         IsInAnonymousNamespace = true;
367       } else
368         Namespace = N->getNameAsString();
369       Namespaces.emplace_back(getUSRForDecl(N), Namespace,
370                               InfoType::IT_namespace);
371     } else if (const auto *N = dyn_cast<RecordDecl>(DC))
372       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
373                               InfoType::IT_record);
374     else if (const auto *N = dyn_cast<FunctionDecl>(DC))
375       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
376                               InfoType::IT_function);
377     else if (const auto *N = dyn_cast<EnumDecl>(DC))
378       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
379                               InfoType::IT_enum);
380   }
381   // The global namespace should be added to the list of namespaces if the decl
382   // corresponds to a Record and if it doesn't have any namespace (because this
383   // means it's in the global namespace). Also if its outermost namespace is a
384   // record because that record matches the previous condition mentioned.
385   if ((Namespaces.empty() && dyn_cast<RecordDecl>(D)) ||
386       (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
387     Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
388                             InfoType::IT_namespace);
389 }
390 
391 template <typename T>
populateInfo(Info & I,const T * D,const FullComment * C,bool & IsInAnonymousNamespace)392 static void populateInfo(Info &I, const T *D, const FullComment *C,
393                          bool &IsInAnonymousNamespace) {
394   I.USR = getUSRForDecl(D);
395   I.Name = D->getNameAsString();
396   populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
397   if (C) {
398     I.Description.emplace_back();
399     parseFullComment(C, I.Description.back());
400   }
401 }
402 
403 template <typename T>
populateSymbolInfo(SymbolInfo & I,const T * D,const FullComment * C,int LineNumber,StringRef Filename,bool IsFileInRootDir,bool & IsInAnonymousNamespace)404 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
405                                int LineNumber, StringRef Filename,
406                                bool IsFileInRootDir,
407                                bool &IsInAnonymousNamespace) {
408   populateInfo(I, D, C, IsInAnonymousNamespace);
409   if (D->isThisDeclarationADefinition())
410     I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
411   else
412     I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
413 }
414 
populateFunctionInfo(FunctionInfo & I,const FunctionDecl * D,const FullComment * FC,int LineNumber,StringRef Filename,bool IsFileInRootDir,bool & IsInAnonymousNamespace)415 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
416                                  const FullComment *FC, int LineNumber,
417                                  StringRef Filename, bool IsFileInRootDir,
418                                  bool &IsInAnonymousNamespace) {
419   populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
420                      IsInAnonymousNamespace);
421   if (const auto *T = getDeclForType(D->getReturnType())) {
422     if (dyn_cast<EnumDecl>(T))
423       I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
424                               InfoType::IT_enum, getInfoRelativePath(T));
425     else if (dyn_cast<RecordDecl>(T))
426       I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
427                               InfoType::IT_record, getInfoRelativePath(T));
428   } else {
429     I.ReturnType = TypeInfo(D->getReturnType().getAsString());
430   }
431   parseParameters(I, D);
432 }
433 
434 static void
parseBases(RecordInfo & I,const CXXRecordDecl * D,bool IsFileInRootDir,bool PublicOnly,bool IsParent,AccessSpecifier ParentAccess=AccessSpecifier::AS_public)435 parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
436            bool PublicOnly, bool IsParent,
437            AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
438   // Don't parse bases if this isn't a definition.
439   if (!D->isThisDeclarationADefinition())
440     return;
441   for (const CXXBaseSpecifier &B : D->bases()) {
442     if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
443       if (const CXXRecordDecl *Base =
444               cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
445         // Initialized without USR and name, this will be set in the following
446         // if-else stmt.
447         BaseRecordInfo BI(
448             {}, "", getInfoRelativePath(Base), B.isVirtual(),
449             getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
450             IsParent);
451         if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
452           const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
453           BI.USR = getUSRForDecl(D);
454           BI.Name = B.getType().getAsString();
455         } else {
456           BI.USR = getUSRForDecl(Base);
457           BI.Name = Base->getNameAsString();
458         }
459         parseFields(BI, Base, PublicOnly, BI.Access);
460         for (const auto &Decl : Base->decls())
461           if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
462             // Don't serialize private methods
463             if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
464                 !MD->isUserProvided())
465               continue;
466             FunctionInfo FI;
467             FI.IsMethod = true;
468             // The seventh arg in populateFunctionInfo is a boolean passed by
469             // reference, its value is not relevant in here so it's not used
470             // anywhere besides the function call.
471             bool IsInAnonymousNamespace;
472             populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
473                                  /*FileName=*/{}, IsFileInRootDir,
474                                  IsInAnonymousNamespace);
475             FI.Access =
476                 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
477             BI.ChildFunctions.emplace_back(std::move(FI));
478           }
479         I.Bases.emplace_back(std::move(BI));
480         // Call this function recursively to get the inherited classes of
481         // this base; these new bases will also get stored in the original
482         // RecordInfo: I.
483         parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
484                    I.Bases.back().Access);
485       }
486     }
487   }
488 }
489 
490 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const NamespaceDecl * D,const FullComment * FC,int LineNumber,llvm::StringRef File,bool IsFileInRootDir,bool PublicOnly)491 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
492          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
493   auto I = std::make_unique<NamespaceInfo>();
494   bool IsInAnonymousNamespace = false;
495   populateInfo(*I, D, FC, IsInAnonymousNamespace);
496   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
497     return {};
498 
499   I->Name = D->isAnonymousNamespace()
500                 ? llvm::SmallString<16>("@nonymous_namespace")
501                 : I->Name;
502   I->Path = getInfoRelativePath(I->Namespace);
503   if (I->Namespace.empty() && I->USR == SymbolID())
504     return {std::unique_ptr<Info>{std::move(I)}, nullptr};
505 
506   auto ParentI = std::make_unique<NamespaceInfo>();
507   ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
508   ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace,
509                                         getInfoRelativePath(I->Namespace));
510   if (I->Namespace.empty())
511     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
512   return {std::unique_ptr<Info>{std::move(I)},
513           std::unique_ptr<Info>{std::move(ParentI)}};
514 }
515 
516 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const RecordDecl * D,const FullComment * FC,int LineNumber,llvm::StringRef File,bool IsFileInRootDir,bool PublicOnly)517 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
518          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
519   auto I = std::make_unique<RecordInfo>();
520   bool IsInAnonymousNamespace = false;
521   populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
522                      IsInAnonymousNamespace);
523   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
524     return {};
525 
526   I->TagType = D->getTagKind();
527   parseFields(*I, D, PublicOnly);
528   if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
529     if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
530       I->Name = TD->getNameAsString();
531       I->IsTypeDef = true;
532     }
533     // TODO: remove first call to parseBases, that function should be deleted
534     parseBases(*I, C);
535     parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
536   }
537   I->Path = getInfoRelativePath(I->Namespace);
538 
539   switch (I->Namespace[0].RefType) {
540   case InfoType::IT_namespace: {
541     auto ParentI = std::make_unique<NamespaceInfo>();
542     ParentI->USR = I->Namespace[0].USR;
543     ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
544                                        getInfoRelativePath(I->Namespace));
545     return {std::unique_ptr<Info>{std::move(I)},
546             std::unique_ptr<Info>{std::move(ParentI)}};
547   }
548   case InfoType::IT_record: {
549     auto ParentI = std::make_unique<RecordInfo>();
550     ParentI->USR = I->Namespace[0].USR;
551     ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
552                                        getInfoRelativePath(I->Namespace));
553     return {std::unique_ptr<Info>{std::move(I)},
554             std::unique_ptr<Info>{std::move(ParentI)}};
555   }
556   default:
557     llvm_unreachable("Invalid reference type for parent namespace");
558   }
559 }
560 
561 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const FunctionDecl * D,const FullComment * FC,int LineNumber,llvm::StringRef File,bool IsFileInRootDir,bool PublicOnly)562 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
563          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
564   FunctionInfo Func;
565   bool IsInAnonymousNamespace = false;
566   populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
567                        IsInAnonymousNamespace);
568   Func.Access = clang::AccessSpecifier::AS_none;
569   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
570     return {};
571 
572   // Wrap in enclosing scope
573   auto ParentI = std::make_unique<NamespaceInfo>();
574   if (!Func.Namespace.empty())
575     ParentI->USR = Func.Namespace[0].USR;
576   else
577     ParentI->USR = SymbolID();
578   if (Func.Namespace.empty())
579     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
580   ParentI->ChildFunctions.emplace_back(std::move(Func));
581   // Info is wrapped in its parent scope so it's returned in the second position
582   return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
583 }
584 
585 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const CXXMethodDecl * D,const FullComment * FC,int LineNumber,llvm::StringRef File,bool IsFileInRootDir,bool PublicOnly)586 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
587          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
588   FunctionInfo Func;
589   bool IsInAnonymousNamespace = false;
590   populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
591                        IsInAnonymousNamespace);
592   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
593     return {};
594 
595   Func.IsMethod = true;
596 
597   const NamedDecl *Parent = nullptr;
598   if (const auto *SD =
599           dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
600     Parent = SD->getSpecializedTemplate();
601   else
602     Parent = D->getParent();
603 
604   SymbolID ParentUSR = getUSRForDecl(Parent);
605   Func.Parent =
606       Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record};
607   Func.Access = D->getAccess();
608 
609   // Wrap in enclosing scope
610   auto ParentI = std::make_unique<RecordInfo>();
611   ParentI->USR = ParentUSR;
612   ParentI->ChildFunctions.emplace_back(std::move(Func));
613   // Info is wrapped in its parent scope so it's returned in the second position
614   return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
615 }
616 
617 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
emitInfo(const EnumDecl * D,const FullComment * FC,int LineNumber,llvm::StringRef File,bool IsFileInRootDir,bool PublicOnly)618 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
619          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
620   EnumInfo Enum;
621   bool IsInAnonymousNamespace = false;
622   populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
623                      IsInAnonymousNamespace);
624   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
625     return {};
626 
627   Enum.Scoped = D->isScoped();
628   parseEnumerators(Enum, D);
629 
630   // Put in global namespace
631   if (Enum.Namespace.empty()) {
632     auto ParentI = std::make_unique<NamespaceInfo>();
633     ParentI->USR = SymbolID();
634     ParentI->ChildEnums.emplace_back(std::move(Enum));
635     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
636     // Info is wrapped in its parent scope so it's returned in the second
637     // position
638     return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
639   }
640 
641   // Wrap in enclosing scope
642   switch (Enum.Namespace[0].RefType) {
643   case InfoType::IT_namespace: {
644     auto ParentI = std::make_unique<NamespaceInfo>();
645     ParentI->USR = Enum.Namespace[0].USR;
646     ParentI->ChildEnums.emplace_back(std::move(Enum));
647     // Info is wrapped in its parent scope so it's returned in the second
648     // position
649     return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
650   }
651   case InfoType::IT_record: {
652     auto ParentI = std::make_unique<RecordInfo>();
653     ParentI->USR = Enum.Namespace[0].USR;
654     ParentI->ChildEnums.emplace_back(std::move(Enum));
655     // Info is wrapped in its parent scope so it's returned in the second
656     // position
657     return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
658   }
659   default:
660     llvm_unreachable("Invalid reference type for parent namespace");
661   }
662 }
663 
664 } // namespace serialize
665 } // namespace doc
666 } // namespace clang
667