[clang-tools-extra] Migrate llvm::make_unique to std::make_unique
[lldb.git] / clang-tools-extra / clang-doc / Serialize.cpp
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
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 diretory path for class C info will be
36 // <root>/A/B
37 //
38 // namespace A {
39 // namesapce B {
40 //
41 // class C {};
42 //
43 // }
44 // }
45 llvm::SmallString<128>
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
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:
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
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
99 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
100   if (!isWhitespaceOnly(C->getText()))
101     CurrentCI.Text = C->getText();
102 }
103
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
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
122 void ClangDocCommentVisitor::visitHTMLEndTagComment(
123     const HTMLEndTagComment *C) {
124   CurrentCI.Name = C->getTagName();
125   CurrentCI.SelfClosing = true;
126 }
127
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
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
144 void ClangDocCommentVisitor::visitTParamCommandComment(
145     const TParamCommandComment *C) {
146   if (C->hasParamName())
147     CurrentCI.ParamName = C->getParamNameAsWritten();
148 }
149
150 void ClangDocCommentVisitor::visitVerbatimBlockComment(
151     const VerbatimBlockComment *C) {
152   CurrentCI.Name = getCommandName(C->getCommandID());
153   CurrentCI.CloseName = C->getCloseName();
154 }
155
156 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
157     const VerbatimBlockLineComment *C) {
158   if (!isWhitespaceOnly(C->getText()))
159     CurrentCI.Text = C->getText();
160 }
161
162 void ClangDocCommentVisitor::visitVerbatimLineComment(
163     const VerbatimLineComment *C) {
164   if (!isWhitespaceOnly(C->getText()))
165     CurrentCI.Text = C->getText();
166 }
167
168 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
169   return std::all_of(S.begin(), S.end(), isspace);
170 }
171
172 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
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
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
205 static void parseFullComment(const FullComment *C, CommentInfo &CI) {
206   ClangDocCommentVisitor Visitor(CI);
207   Visitor.parseComment(C);
208 }
209
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
217 static RecordDecl *getDeclForType(const QualType &T) {
218   if (const RecordDecl *D = T->getAsRecordDecl())
219     return D->getDefinition();
220   return nullptr;
221 }
222
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
233 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) {
234   for (const FieldDecl *F : D->fields()) {
235     if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal()))
236       continue;
237     if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
238       // Use getAccessUnsafe so that we just get the default AS_none if it's not
239       // valid, as opposed to an assert.
240       if (const auto *N = dyn_cast<EnumDecl>(T)) {
241         I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
242                                InfoType::IT_enum, getInfoRelativePath(N),
243                                F->getNameAsString(), N->getAccessUnsafe());
244         continue;
245       } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
246         I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
247                                InfoType::IT_record, getInfoRelativePath(N),
248                                F->getNameAsString(), N->getAccessUnsafe());
249         continue;
250       }
251     }
252     I.Members.emplace_back(F->getTypeSourceInfo()->getType().getAsString(),
253                            F->getNameAsString(), F->getAccessUnsafe());
254   }
255 }
256
257 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
258   for (const EnumConstantDecl *E : D->enumerators())
259     I.Members.emplace_back(E->getNameAsString());
260 }
261
262 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
263   for (const ParmVarDecl *P : D->parameters()) {
264     if (const auto *T = getDeclForType(P->getOriginalType())) {
265       if (const auto *N = dyn_cast<EnumDecl>(T)) {
266         I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
267                               InfoType::IT_enum, getInfoRelativePath(N),
268                               P->getNameAsString());
269         continue;
270       } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
271         I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
272                               InfoType::IT_record, getInfoRelativePath(N),
273                               P->getNameAsString());
274         continue;
275       }
276     }
277     I.Params.emplace_back(P->getOriginalType().getAsString(),
278                           P->getNameAsString());
279   }
280 }
281
282 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
283   // Don't parse bases if this isn't a definition.
284   if (!D->isThisDeclarationADefinition())
285     return;
286   for (const CXXBaseSpecifier &B : D->bases()) {
287     if (B.isVirtual())
288       continue;
289     if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
290       const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
291       I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
292                              InfoType::IT_record);
293     } else if (const RecordDecl *P = getDeclForType(B.getType()))
294       I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
295                              InfoType::IT_record, getInfoRelativePath(P));
296     else
297       I.Parents.emplace_back(B.getType().getAsString());
298   }
299   for (const CXXBaseSpecifier &B : D->vbases()) {
300     if (const auto *P = getDeclForType(B.getType()))
301       I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
302                                     InfoType::IT_record,
303                                     getInfoRelativePath(P));
304     else
305       I.VirtualParents.emplace_back(B.getType().getAsString());
306   }
307 }
308
309 template <typename T>
310 static void
311 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
312                          const T *D, bool &IsInAnonymousNamespace) {
313   const auto *DC = dyn_cast<DeclContext>(D);
314   while ((DC = DC->getParent())) {
315     if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
316       std::string Namespace;
317       if (N->isAnonymousNamespace()) {
318         Namespace = "@nonymous_namespace";
319         IsInAnonymousNamespace = true;
320       } else
321         Namespace = N->getNameAsString();
322       Namespaces.emplace_back(getUSRForDecl(N), Namespace,
323                               InfoType::IT_namespace);
324     } else if (const auto *N = dyn_cast<RecordDecl>(DC))
325       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
326                               InfoType::IT_record);
327     else if (const auto *N = dyn_cast<FunctionDecl>(DC))
328       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
329                               InfoType::IT_function);
330     else if (const auto *N = dyn_cast<EnumDecl>(DC))
331       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
332                               InfoType::IT_enum);
333   }
334 }
335
336 template <typename T>
337 static void populateInfo(Info &I, const T *D, const FullComment *C,
338                          bool &IsInAnonymousNamespace) {
339   I.USR = getUSRForDecl(D);
340   I.Name = D->getNameAsString();
341   populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
342   if (C) {
343     I.Description.emplace_back();
344     parseFullComment(C, I.Description.back());
345   }
346 }
347
348 template <typename T>
349 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
350                                int LineNumber, StringRef Filename,
351                                bool IsFileInRootDir,
352                                bool &IsInAnonymousNamespace) {
353   populateInfo(I, D, C, IsInAnonymousNamespace);
354   if (D->isThisDeclarationADefinition())
355     I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
356   else
357     I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
358 }
359
360 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
361                                  const FullComment *FC, int LineNumber,
362                                  StringRef Filename, bool IsFileInRootDir,
363                                  bool &IsInAnonymousNamespace) {
364   populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
365                      IsInAnonymousNamespace);
366   if (const auto *T = getDeclForType(D->getReturnType())) {
367     if (dyn_cast<EnumDecl>(T))
368       I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
369                               InfoType::IT_enum, getInfoRelativePath(T));
370     else if (dyn_cast<RecordDecl>(T))
371       I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
372                               InfoType::IT_record, getInfoRelativePath(T));
373   } else {
374     I.ReturnType = TypeInfo(D->getReturnType().getAsString());
375   }
376   parseParameters(I, D);
377 }
378
379 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
380 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
381          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
382   auto I = std::make_unique<NamespaceInfo>();
383   bool IsInAnonymousNamespace = false;
384   populateInfo(*I, D, FC, IsInAnonymousNamespace);
385   if (PublicOnly && ((IsInAnonymousNamespace || D->isAnonymousNamespace()) ||
386                      !isPublic(D->getAccess(), D->getLinkageInternal())))
387     return {};
388   I->Name = D->isAnonymousNamespace()
389                 ? llvm::SmallString<16>("@nonymous_namespace")
390                 : I->Name;
391   I->Path = getInfoRelativePath(I->Namespace);
392   if (I->Namespace.empty() && I->USR == SymbolID())
393     return {std::unique_ptr<Info>{std::move(I)}, nullptr};
394
395   auto ParentI = std::make_unique<NamespaceInfo>();
396   ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
397   ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace,
398                                         getInfoRelativePath(I->Namespace));
399   if (I->Namespace.empty())
400     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
401   return {std::unique_ptr<Info>{std::move(I)},
402           std::unique_ptr<Info>{std::move(ParentI)}};
403 }
404
405 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
406 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
407          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
408   auto I = std::make_unique<RecordInfo>();
409   bool IsInAnonymousNamespace = false;
410   populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
411                      IsInAnonymousNamespace);
412   if (PublicOnly && ((IsInAnonymousNamespace ||
413                       !isPublic(D->getAccess(), D->getLinkageInternal()))))
414     return {};
415
416   I->TagType = D->getTagKind();
417   parseFields(*I, D, PublicOnly);
418   if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
419     if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
420       I->Name = TD->getNameAsString();
421       I->IsTypeDef = true;
422     }
423     parseBases(*I, C);
424   }
425   I->Path = getInfoRelativePath(I->Namespace);
426
427   if (I->Namespace.empty()) {
428     auto ParentI = std::make_unique<NamespaceInfo>();
429     ParentI->USR = SymbolID();
430     ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
431                                        getInfoRelativePath(I->Namespace));
432     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
433     return {std::unique_ptr<Info>{std::move(I)},
434             std::unique_ptr<Info>{std::move(ParentI)}};
435   }
436
437   switch (I->Namespace[0].RefType) {
438   case InfoType::IT_namespace: {
439     auto ParentI = std::make_unique<NamespaceInfo>();
440     ParentI->USR = I->Namespace[0].USR;
441     ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
442                                        getInfoRelativePath(I->Namespace));
443     return {std::unique_ptr<Info>{std::move(I)},
444             std::unique_ptr<Info>{std::move(ParentI)}};
445   }
446   case InfoType::IT_record: {
447     auto ParentI = std::make_unique<RecordInfo>();
448     ParentI->USR = I->Namespace[0].USR;
449     ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
450                                        getInfoRelativePath(I->Namespace));
451     return {std::unique_ptr<Info>{std::move(I)},
452             std::unique_ptr<Info>{std::move(ParentI)}};
453   }
454   default:
455     llvm_unreachable("Invalid reference type for parent namespace");
456   }
457 }
458
459 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
460 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
461          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
462   FunctionInfo Func;
463   bool IsInAnonymousNamespace = false;
464   populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
465                        IsInAnonymousNamespace);
466   if (PublicOnly && ((IsInAnonymousNamespace ||
467                       !isPublic(D->getAccess(), D->getLinkageInternal()))))
468     return {};
469
470   Func.Access = clang::AccessSpecifier::AS_none;
471
472   // Wrap in enclosing scope
473   auto ParentI = std::make_unique<NamespaceInfo>();
474   if (!Func.Namespace.empty())
475     ParentI->USR = Func.Namespace[0].USR;
476   else
477     ParentI->USR = SymbolID();
478   if (Func.Namespace.empty())
479     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
480   ParentI->ChildFunctions.emplace_back(std::move(Func));
481   // Info is wrapped in its parent scope so it's returned in the second position
482   return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
483 }
484
485 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
486 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
487          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
488   FunctionInfo Func;
489   bool IsInAnonymousNamespace = false;
490   populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
491                        IsInAnonymousNamespace);
492   if (PublicOnly && ((IsInAnonymousNamespace ||
493                       !isPublic(D->getAccess(), D->getLinkageInternal()))))
494     return {};
495
496   Func.IsMethod = true;
497
498   const NamedDecl *Parent = nullptr;
499   if (const auto *SD =
500           dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
501     Parent = SD->getSpecializedTemplate();
502   else
503     Parent = D->getParent();
504
505   SymbolID ParentUSR = getUSRForDecl(Parent);
506   Func.Parent =
507       Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record};
508   Func.Access = D->getAccess();
509
510   // Wrap in enclosing scope
511   auto ParentI = std::make_unique<RecordInfo>();
512   ParentI->USR = ParentUSR;
513   if (Func.Namespace.empty())
514     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
515   ParentI->ChildFunctions.emplace_back(std::move(Func));
516   // Info is wrapped in its parent scope so it's returned in the second position
517   return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
518 }
519
520 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
521 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
522          llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
523   EnumInfo Enum;
524   bool IsInAnonymousNamespace = false;
525   populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
526                      IsInAnonymousNamespace);
527   if (PublicOnly && ((IsInAnonymousNamespace ||
528                       !isPublic(D->getAccess(), D->getLinkageInternal()))))
529     return {};
530
531   Enum.Scoped = D->isScoped();
532   parseEnumerators(Enum, D);
533
534   // Put in global namespace
535   if (Enum.Namespace.empty()) {
536     auto ParentI = std::make_unique<NamespaceInfo>();
537     ParentI->USR = SymbolID();
538     ParentI->ChildEnums.emplace_back(std::move(Enum));
539     ParentI->Path = getInfoRelativePath(ParentI->Namespace);
540     // Info is wrapped in its parent scope so it's returned in the second
541     // position
542     return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
543   }
544
545   // Wrap in enclosing scope
546   switch (Enum.Namespace[0].RefType) {
547   case InfoType::IT_namespace: {
548     auto ParentI = std::make_unique<NamespaceInfo>();
549     ParentI->USR = Enum.Namespace[0].USR;
550     ParentI->ChildEnums.emplace_back(std::move(Enum));
551     // Info is wrapped in its parent scope so it's returned in the second
552     // position
553     return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
554   }
555   case InfoType::IT_record: {
556     auto ParentI = std::make_unique<RecordInfo>();
557     ParentI->USR = Enum.Namespace[0].USR;
558     ParentI->ChildEnums.emplace_back(std::move(Enum));
559     // Info is wrapped in its parent scope so it's returned in the second
560     // position
561     return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
562   }
563   default:
564     llvm_unreachable("Invalid reference type for parent namespace");
565   }
566 }
567
568 } // namespace serialize
569 } // namespace doc
570 } // namespace clang