1 //===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
17 using clang::comments::FullComment
;
23 SymbolID
hashUSR(llvm::StringRef USR
) {
24 return llvm::SHA1::hash(arrayRefFromStringRef(USR
));
29 populateParentNamespaces(llvm::SmallVector
<Reference
, 4> &Namespaces
,
30 const T
*D
, bool &IsAnonymousNamespace
);
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.
35 // Example: Given the below, the directory path for class C info will be
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
);
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
59 populateParentNamespaces(Namespaces
, D
, B
);
60 return getInfoRelativePath(Namespaces
);
63 class ClangDocCommentVisitor
64 : public ConstCommentVisitor
<ClangDocCommentVisitor
> {
66 ClangDocCommentVisitor(CommentInfo
&CI
) : CurrentCI(CI
) {}
68 void parseComment(const comments::Comment
*C
);
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
);
82 std::string
getCommandName(unsigned CommandID
) const;
83 bool isWhitespaceOnly(StringRef S
) const;
85 CommentInfo
&CurrentCI
;
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
);
99 void ClangDocCommentVisitor::visitTextComment(const TextComment
*C
) {
100 if (!isWhitespaceOnly(C
->getText()))
101 CurrentCI
.Text
= C
->getText();
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
));
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
);
122 void ClangDocCommentVisitor::visitHTMLEndTagComment(
123 const HTMLEndTagComment
*C
) {
124 CurrentCI
.Name
= C
->getTagName();
125 CurrentCI
.SelfClosing
= true;
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
));
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();
144 void ClangDocCommentVisitor::visitTParamCommandComment(
145 const TParamCommandComment
*C
) {
146 if (C
->hasParamName())
147 CurrentCI
.ParamName
= C
->getParamNameAsWritten();
150 void ClangDocCommentVisitor::visitVerbatimBlockComment(
151 const VerbatimBlockComment
*C
) {
152 CurrentCI
.Name
= getCommandName(C
->getCommandID());
153 CurrentCI
.CloseName
= C
->getCloseName();
156 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
157 const VerbatimBlockLineComment
*C
) {
158 if (!isWhitespaceOnly(C
->getText()))
159 CurrentCI
.Text
= C
->getText();
162 void ClangDocCommentVisitor::visitVerbatimLineComment(
163 const VerbatimLineComment
*C
) {
164 if (!isWhitespaceOnly(C
->getText()))
165 CurrentCI
.Text
= C
->getText();
168 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S
) const {
169 return std::all_of(S
.begin(), S
.end(), isspace
);
172 std::string
ClangDocCommentVisitor::getCommandName(unsigned CommandID
) const {
173 const CommandInfo
*Info
= CommandTraits::getBuiltinCommandInfo(CommandID
);
176 // TODO: Add parsing for \file command.
177 return "<not a builtin command>";
180 // Serializing functions.
182 template <typename T
> static std::string
serialize(T
&I
) {
183 SmallString
<2048> Buffer
;
184 llvm::BitstreamWriter
Stream(Buffer
);
185 ClangDocBitcodeWriter
Writer(Stream
);
187 return Buffer
.str().str();
190 std::string
serialize(std::unique_ptr
<Info
> &I
) {
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()));
205 static void parseFullComment(const FullComment
*C
, CommentInfo
&CI
) {
206 ClangDocCommentVisitor
Visitor(CI
);
207 Visitor
.parseComment(C
);
210 static SymbolID
getUSRForDecl(const Decl
*D
) {
211 llvm::SmallString
<128> USR
;
212 if (index::generateUSRForDecl(D
, USR
))
217 static RecordDecl
*getDeclForType(const QualType
&T
) {
218 if (const RecordDecl
*D
= T
->getAsRecordDecl())
219 return D
->getDefinition();
223 static bool isPublic(const clang::AccessSpecifier AS
,
224 const clang::Linkage Link
) {
225 if (AS
== clang::AccessSpecifier::AS_private
)
227 else if ((Link
== clang::Linkage::ModuleLinkage
) ||
228 (Link
== clang::Linkage::ExternalLinkage
))
230 return false; // otherwise, linkage is some form of internal linkage
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()));
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
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.
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
;
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
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
))
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()));
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()));
296 I
.Members
.emplace_back(
297 F
->getTypeSourceInfo()->getType().getAsString(), F
->getNameAsString(),
298 getFinalAccessSpecifier(Access
, F
->getAccessUnsafe()));
302 static void parseEnumerators(EnumInfo
&I
, const EnumDecl
*D
) {
303 for (const EnumConstantDecl
*E
: D
->enumerators())
304 I
.Members
.emplace_back(E
->getNameAsString());
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());
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());
322 I
.Params
.emplace_back(P
->getOriginalType().getAsString(),
323 P
->getNameAsString());
327 // TODO: Remove the serialization of Parents and VirtualParents, this
328 // information is also extracted in the other definition of parseBases.
329 static void parseBases(RecordInfo
&I
, const CXXRecordDecl
*D
) {
330 // Don't parse bases if this isn't a definition.
331 if (!D
->isThisDeclarationADefinition())
333 for (const CXXBaseSpecifier
&B
: D
->bases()) {
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
));
344 I
.Parents
.emplace_back(B
.getType().getAsString());
346 for (const CXXBaseSpecifier
&B
: D
->vbases()) {
347 if (const auto *P
= getDeclForType(B
.getType()))
348 I
.VirtualParents
.emplace_back(getUSRForDecl(P
), P
->getNameAsString(),
350 getInfoRelativePath(P
));
352 I
.VirtualParents
.emplace_back(B
.getType().getAsString());
356 template <typename T
>
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;
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(),
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() && isa
<RecordDecl
>(D
)) ||
386 (!Namespaces
.empty() && Namespaces
.back().RefType
== InfoType::IT_record
))
387 Namespaces
.emplace_back(SymbolID(), "GlobalNamespace",
388 InfoType::IT_namespace
);
391 template <typename T
>
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
);
398 I
.Description
.emplace_back();
399 parseFullComment(C
, I
.Description
.back());
403 template <typename T
>
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
);
412 I
.Loc
.emplace_back(LineNumber
, Filename
, IsFileInRootDir
);
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 (isa
<EnumDecl
>(T
))
423 I
.ReturnType
= TypeInfo(getUSRForDecl(T
), T
->getNameAsString(),
424 InfoType::IT_enum
, getInfoRelativePath(T
));
425 else if (isa
<RecordDecl
>(T
))
426 I
.ReturnType
= TypeInfo(getUSRForDecl(T
), T
->getNameAsString(),
427 InfoType::IT_record
, getInfoRelativePath(T
));
429 I
.ReturnType
= TypeInfo(D
->getReturnType().getAsString());
431 parseParameters(I
, D
);
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())
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
448 {}, "", getInfoRelativePath(Base
), B
.isVirtual(),
449 getFinalAccessSpecifier(ParentAccess
, B
.getAccessSpecifier()),
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();
456 BI
.USR
= getUSRForDecl(Base
);
457 BI
.Name
= Base
->getNameAsString();
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())
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
);
476 getFinalAccessSpecifier(BI
.Access
, MD
->getAccessUnsafe());
477 BI
.ChildFunctions
.emplace_back(std::move(FI
));
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
483 parseBases(I
, Base
, IsFileInRootDir
, PublicOnly
, false,
484 I
.Bases
.back().Access
);
490 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
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
))
499 I
->Name
= D
->isAnonymousNamespace()
500 ? llvm::SmallString
<16>("@nonymous_namespace")
502 I
->Path
= getInfoRelativePath(I
->Namespace
);
503 if (I
->Namespace
.empty() && I
->USR
== SymbolID())
504 return {std::unique_ptr
<Info
>{std::move(I
)}, nullptr};
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
)}};
516 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
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
))
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();
533 // TODO: remove first call to parseBases, that function should be deleted
535 parseBases(*I
, C
, IsFileInRootDir
, PublicOnly
, true);
537 I
->Path
= getInfoRelativePath(I
->Namespace
);
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
)}};
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
)}};
557 llvm_unreachable("Invalid reference type for parent namespace");
561 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
562 emitInfo(const FunctionDecl
*D
, const FullComment
*FC
, int LineNumber
,
563 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
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
))
572 // Wrap in enclosing scope
573 auto ParentI
= std::make_unique
<NamespaceInfo
>();
574 if (!Func
.Namespace
.empty())
575 ParentI
->USR
= Func
.Namespace
[0].USR
;
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
)}};
585 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
586 emitInfo(const CXXMethodDecl
*D
, const FullComment
*FC
, int LineNumber
,
587 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
589 bool IsInAnonymousNamespace
= false;
590 populateFunctionInfo(Func
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
591 IsInAnonymousNamespace
);
592 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
595 Func
.IsMethod
= true;
597 const NamedDecl
*Parent
= nullptr;
599 dyn_cast
<ClassTemplateSpecializationDecl
>(D
->getParent()))
600 Parent
= SD
->getSpecializedTemplate();
602 Parent
= D
->getParent();
604 SymbolID ParentUSR
= getUSRForDecl(Parent
);
606 Reference
{ParentUSR
, Parent
->getNameAsString(), InfoType::IT_record
};
607 Func
.Access
= D
->getAccess();
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
)}};
617 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
618 emitInfo(const EnumDecl
*D
, const FullComment
*FC
, int LineNumber
,
619 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
621 bool IsInAnonymousNamespace
= false;
622 populateSymbolInfo(Enum
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
623 IsInAnonymousNamespace
);
624 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
627 Enum
.Scoped
= D
->isScoped();
628 parseEnumerators(Enum
, D
);
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
638 return {nullptr, std::unique_ptr
<Info
>{std::move(ParentI
)}};
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
649 return {nullptr, std::unique_ptr
<Info
>{std::move(ParentI
)}};
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
657 return {nullptr, std::unique_ptr
<Info
>{std::move(ParentI
)}};
660 llvm_unreachable("Invalid reference type for parent namespace");
664 } // namespace serialize