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 "clang/Lex/Lexer.h"
14 #include "llvm/ADT/Hashing.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/Support/SHA1.h"
18 using clang::comments::FullComment
;
24 SymbolID
hashUSR(llvm::StringRef USR
) {
25 return llvm::SHA1::hash(arrayRefFromStringRef(USR
));
30 populateParentNamespaces(llvm::SmallVector
<Reference
, 4> &Namespaces
,
31 const T
*D
, bool &IsAnonymousNamespace
);
33 static void populateMemberTypeInfo(MemberTypeInfo
&I
, const FieldDecl
*D
);
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.
38 // Example: Given the below, the directory path for class C info will be
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
);
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
62 populateParentNamespaces(Namespaces
, D
, B
);
63 return getInfoRelativePath(Namespaces
);
66 class ClangDocCommentVisitor
67 : public ConstCommentVisitor
<ClangDocCommentVisitor
> {
69 ClangDocCommentVisitor(CommentInfo
&CI
) : CurrentCI(CI
) {}
71 void parseComment(const comments::Comment
*C
);
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
);
85 std::string
getCommandName(unsigned CommandID
) const;
86 bool isWhitespaceOnly(StringRef S
) const;
88 CommentInfo
&CurrentCI
;
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
);
102 void ClangDocCommentVisitor::visitTextComment(const TextComment
*C
) {
103 if (!isWhitespaceOnly(C
->getText()))
104 CurrentCI
.Text
= C
->getText();
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
));
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
);
125 void ClangDocCommentVisitor::visitHTMLEndTagComment(
126 const HTMLEndTagComment
*C
) {
127 CurrentCI
.Name
= C
->getTagName();
128 CurrentCI
.SelfClosing
= true;
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
));
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();
147 void ClangDocCommentVisitor::visitTParamCommandComment(
148 const TParamCommandComment
*C
) {
149 if (C
->hasParamName())
150 CurrentCI
.ParamName
= C
->getParamNameAsWritten();
153 void ClangDocCommentVisitor::visitVerbatimBlockComment(
154 const VerbatimBlockComment
*C
) {
155 CurrentCI
.Name
= getCommandName(C
->getCommandID());
156 CurrentCI
.CloseName
= C
->getCloseName();
159 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
160 const VerbatimBlockLineComment
*C
) {
161 if (!isWhitespaceOnly(C
->getText()))
162 CurrentCI
.Text
= C
->getText();
165 void ClangDocCommentVisitor::visitVerbatimLineComment(
166 const VerbatimLineComment
*C
) {
167 if (!isWhitespaceOnly(C
->getText()))
168 CurrentCI
.Text
= C
->getText();
171 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S
) const {
172 return llvm::all_of(S
, isspace
);
175 std::string
ClangDocCommentVisitor::getCommandName(unsigned CommandID
) const {
176 const CommandInfo
*Info
= CommandTraits::getBuiltinCommandInfo(CommandID
);
179 // TODO: Add parsing for \file command.
180 return "<not a builtin command>";
183 // Serializing functions.
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())
192 template <typename T
> static std::string
serialize(T
&I
) {
193 SmallString
<2048> Buffer
;
194 llvm::BitstreamWriter
Stream(Buffer
);
195 ClangDocBitcodeWriter
Writer(Stream
);
197 return Buffer
.str().str();
200 std::string
serialize(std::unique_ptr
<Info
> &I
) {
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()));
215 static void parseFullComment(const FullComment
*C
, CommentInfo
&CI
) {
216 ClangDocCommentVisitor
Visitor(CI
);
217 Visitor
.parseComment(C
);
220 static SymbolID
getUSRForDecl(const Decl
*D
) {
221 llvm::SmallString
<128> USR
;
222 if (index::generateUSRForDecl(D
, USR
))
227 static TagDecl
*getTagDeclForType(const QualType
&T
) {
228 if (const TagDecl
*D
= T
->getAsTagDecl())
229 return D
->getDefinition();
233 static RecordDecl
*getRecordDeclForType(const QualType
&T
) {
234 if (const RecordDecl
*D
= T
->getAsRecordDecl())
235 return D
->getDefinition();
239 TypeInfo
getTypeInfoForType(const QualType
&T
) {
240 const TagDecl
*TD
= getTagDeclForType(T
);
242 return TypeInfo(Reference(SymbolID(), T
.getAsString()));
245 if (dyn_cast
<EnumDecl
>(TD
)) {
246 IT
= InfoType::IT_enum
;
247 } else if (dyn_cast
<RecordDecl
>(TD
)) {
248 IT
= InfoType::IT_record
;
250 IT
= InfoType::IT_default
;
252 return TypeInfo(Reference(getUSRForDecl(TD
), TD
->getNameAsString(), IT
,
253 T
.getAsString(), getInfoRelativePath(TD
)));
256 static bool isPublic(const clang::AccessSpecifier AS
,
257 const clang::Linkage Link
) {
258 if (AS
== clang::AccessSpecifier::AS_private
)
260 else if ((Link
== clang::Linkage::Module
) ||
261 (Link
== clang::Linkage::External
))
263 return false; // otherwise, linkage is some form of internal linkage
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()));
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
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
));
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
));
292 static void InsertChild(ScopeChildren
&Scope
, EnumInfo Info
) {
293 Scope
.Enums
.push_back(std::move(Info
));
296 static void InsertChild(ScopeChildren
&Scope
, FunctionInfo Info
) {
297 Scope
.Functions
.push_back(std::move(Info
));
300 static void InsertChild(ScopeChildren
&Scope
, TypedefInfo Info
) {
301 Scope
.Typedefs
.push_back(std::move(Info
));
304 // Creates a parent of the correct type for the given child and inserts it into
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
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
));
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
));
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
));
341 llvm_unreachable("Invalid reference type for parent namespace");
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
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
;
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
))
382 // Use getAccessUnsafe so that we just get the default AS_none if it's not
383 // valid, as opposed to an assert.
384 MemberTypeInfo
&NewMember
= I
.Members
.emplace_back(
385 getTypeInfoForType(F
->getTypeSourceInfo()->getType()),
386 F
->getNameAsString(),
387 getFinalAccessSpecifier(Access
, F
->getAccessUnsafe()));
388 populateMemberTypeInfo(NewMember
, F
);
392 static void parseEnumerators(EnumInfo
&I
, const EnumDecl
*D
) {
393 for (const EnumConstantDecl
*E
: D
->enumerators()) {
394 std::string ValueExpr
;
395 if (const Expr
*InitExpr
= E
->getInitExpr())
396 ValueExpr
= getSourceCode(D
, InitExpr
->getSourceRange());
398 SmallString
<16> ValueStr
;
399 E
->getInitVal().toString(ValueStr
);
400 I
.Members
.emplace_back(E
->getNameAsString(), ValueStr
, ValueExpr
);
404 static void parseParameters(FunctionInfo
&I
, const FunctionDecl
*D
) {
405 for (const ParmVarDecl
*P
: D
->parameters()) {
406 FieldTypeInfo
&FieldInfo
= I
.Params
.emplace_back(
407 getTypeInfoForType(P
->getOriginalType()), P
->getNameAsString());
408 FieldInfo
.DefaultValue
= getSourceCode(D
, P
->getDefaultArgRange());
412 // TODO: Remove the serialization of Parents and VirtualParents, this
413 // information is also extracted in the other definition of parseBases.
414 static void parseBases(RecordInfo
&I
, const CXXRecordDecl
*D
) {
415 // Don't parse bases if this isn't a definition.
416 if (!D
->isThisDeclarationADefinition())
418 for (const CXXBaseSpecifier
&B
: D
->bases()) {
421 if (const auto *Ty
= B
.getType()->getAs
<TemplateSpecializationType
>()) {
422 const TemplateDecl
*D
= Ty
->getTemplateName().getAsTemplateDecl();
423 I
.Parents
.emplace_back(getUSRForDecl(D
), B
.getType().getAsString(),
424 InfoType::IT_record
, B
.getType().getAsString());
425 } else if (const RecordDecl
*P
= getRecordDeclForType(B
.getType()))
426 I
.Parents
.emplace_back(getUSRForDecl(P
), P
->getNameAsString(),
427 InfoType::IT_record
, P
->getQualifiedNameAsString(),
428 getInfoRelativePath(P
));
430 I
.Parents
.emplace_back(SymbolID(), B
.getType().getAsString());
432 for (const CXXBaseSpecifier
&B
: D
->vbases()) {
433 if (const RecordDecl
*P
= getRecordDeclForType(B
.getType()))
434 I
.VirtualParents
.emplace_back(
435 getUSRForDecl(P
), P
->getNameAsString(), InfoType::IT_record
,
436 P
->getQualifiedNameAsString(), getInfoRelativePath(P
));
438 I
.VirtualParents
.emplace_back(SymbolID(), B
.getType().getAsString());
442 template <typename T
>
444 populateParentNamespaces(llvm::SmallVector
<Reference
, 4> &Namespaces
,
445 const T
*D
, bool &IsInAnonymousNamespace
) {
446 const DeclContext
*DC
= D
->getDeclContext();
448 if (const auto *N
= dyn_cast
<NamespaceDecl
>(DC
)) {
449 std::string Namespace
;
450 if (N
->isAnonymousNamespace()) {
451 Namespace
= "@nonymous_namespace";
452 IsInAnonymousNamespace
= true;
454 Namespace
= N
->getNameAsString();
455 Namespaces
.emplace_back(getUSRForDecl(N
), Namespace
,
456 InfoType::IT_namespace
,
457 N
->getQualifiedNameAsString());
458 } else if (const auto *N
= dyn_cast
<RecordDecl
>(DC
))
459 Namespaces
.emplace_back(getUSRForDecl(N
), N
->getNameAsString(),
461 N
->getQualifiedNameAsString());
462 else if (const auto *N
= dyn_cast
<FunctionDecl
>(DC
))
463 Namespaces
.emplace_back(getUSRForDecl(N
), N
->getNameAsString(),
464 InfoType::IT_function
,
465 N
->getQualifiedNameAsString());
466 else if (const auto *N
= dyn_cast
<EnumDecl
>(DC
))
467 Namespaces
.emplace_back(getUSRForDecl(N
), N
->getNameAsString(),
468 InfoType::IT_enum
, N
->getQualifiedNameAsString());
469 } while ((DC
= DC
->getParent()));
470 // The global namespace should be added to the list of namespaces if the decl
471 // corresponds to a Record and if it doesn't have any namespace (because this
472 // means it's in the global namespace). Also if its outermost namespace is a
473 // record because that record matches the previous condition mentioned.
474 if ((Namespaces
.empty() && isa
<RecordDecl
>(D
)) ||
475 (!Namespaces
.empty() && Namespaces
.back().RefType
== InfoType::IT_record
))
476 Namespaces
.emplace_back(SymbolID(), "GlobalNamespace",
477 InfoType::IT_namespace
);
480 void PopulateTemplateParameters(std::optional
<TemplateInfo
> &TemplateInfo
,
481 const clang::Decl
*D
) {
482 if (const TemplateParameterList
*ParamList
=
483 D
->getDescribedTemplateParams()) {
485 TemplateInfo
.emplace();
487 for (const NamedDecl
*ND
: *ParamList
) {
488 TemplateInfo
->Params
.emplace_back(
489 getSourceCode(ND
, ND
->getSourceRange()));
494 TemplateParamInfo
TemplateArgumentToInfo(const clang::Decl
*D
,
495 const TemplateArgument
&Arg
) {
496 // The TemplateArgument's pretty printing handles all the normal cases
497 // well enough for our requirements.
499 llvm::raw_string_ostream
Stream(Str
);
500 Arg
.print(PrintingPolicy(D
->getLangOpts()), Stream
, false);
501 return TemplateParamInfo(Str
);
504 template <typename T
>
505 static void populateInfo(Info
&I
, const T
*D
, const FullComment
*C
,
506 bool &IsInAnonymousNamespace
) {
507 I
.USR
= getUSRForDecl(D
);
508 I
.Name
= D
->getNameAsString();
509 populateParentNamespaces(I
.Namespace
, D
, IsInAnonymousNamespace
);
511 I
.Description
.emplace_back();
512 parseFullComment(C
, I
.Description
.back());
516 template <typename T
>
517 static void populateSymbolInfo(SymbolInfo
&I
, const T
*D
, const FullComment
*C
,
518 int LineNumber
, StringRef Filename
,
519 bool IsFileInRootDir
,
520 bool &IsInAnonymousNamespace
) {
521 populateInfo(I
, D
, C
, IsInAnonymousNamespace
);
522 if (D
->isThisDeclarationADefinition())
523 I
.DefLoc
.emplace(LineNumber
, Filename
, IsFileInRootDir
);
525 I
.Loc
.emplace_back(LineNumber
, Filename
, IsFileInRootDir
);
528 static void populateFunctionInfo(FunctionInfo
&I
, const FunctionDecl
*D
,
529 const FullComment
*FC
, int LineNumber
,
530 StringRef Filename
, bool IsFileInRootDir
,
531 bool &IsInAnonymousNamespace
) {
532 populateSymbolInfo(I
, D
, FC
, LineNumber
, Filename
, IsFileInRootDir
,
533 IsInAnonymousNamespace
);
534 I
.ReturnType
= getTypeInfoForType(D
->getReturnType());
535 parseParameters(I
, D
);
537 PopulateTemplateParameters(I
.Template
, D
);
539 // Handle function template specializations.
540 if (const FunctionTemplateSpecializationInfo
*FTSI
=
541 D
->getTemplateSpecializationInfo()) {
543 I
.Template
.emplace();
544 I
.Template
->Specialization
.emplace();
545 auto &Specialization
= *I
.Template
->Specialization
;
547 Specialization
.SpecializationOf
= getUSRForDecl(FTSI
->getTemplate());
549 // Template parameters to the specialization.
550 if (FTSI
->TemplateArguments
) {
551 for (const TemplateArgument
&Arg
: FTSI
->TemplateArguments
->asArray()) {
552 Specialization
.Params
.push_back(TemplateArgumentToInfo(D
, Arg
));
558 static void populateMemberTypeInfo(MemberTypeInfo
&I
, const FieldDecl
*D
) {
559 assert(D
&& "Expect non-null FieldDecl in populateMemberTypeInfo");
561 ASTContext
& Context
= D
->getASTContext();
562 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
563 // of this logic. See also similar code in Mapper.cpp.
564 RawComment
*Comment
= Context
.getRawCommentForDeclNoCache(D
);
568 Comment
->setAttached();
569 if (comments::FullComment
* fc
= Comment
->parse(Context
, nullptr, D
)) {
570 I
.Description
.emplace_back();
571 parseFullComment(fc
, I
.Description
.back());
576 parseBases(RecordInfo
&I
, const CXXRecordDecl
*D
, bool IsFileInRootDir
,
577 bool PublicOnly
, bool IsParent
,
578 AccessSpecifier ParentAccess
= AccessSpecifier::AS_public
) {
579 // Don't parse bases if this isn't a definition.
580 if (!D
->isThisDeclarationADefinition())
582 for (const CXXBaseSpecifier
&B
: D
->bases()) {
583 if (const RecordType
*Ty
= B
.getType()->getAs
<RecordType
>()) {
584 if (const CXXRecordDecl
*Base
=
585 cast_or_null
<CXXRecordDecl
>(Ty
->getDecl()->getDefinition())) {
586 // Initialized without USR and name, this will be set in the following
589 {}, "", getInfoRelativePath(Base
), B
.isVirtual(),
590 getFinalAccessSpecifier(ParentAccess
, B
.getAccessSpecifier()),
592 if (const auto *Ty
= B
.getType()->getAs
<TemplateSpecializationType
>()) {
593 const TemplateDecl
*D
= Ty
->getTemplateName().getAsTemplateDecl();
594 BI
.USR
= getUSRForDecl(D
);
595 BI
.Name
= B
.getType().getAsString();
597 BI
.USR
= getUSRForDecl(Base
);
598 BI
.Name
= Base
->getNameAsString();
600 parseFields(BI
, Base
, PublicOnly
, BI
.Access
);
601 for (const auto &Decl
: Base
->decls())
602 if (const auto *MD
= dyn_cast
<CXXMethodDecl
>(Decl
)) {
603 // Don't serialize private methods
604 if (MD
->getAccessUnsafe() == AccessSpecifier::AS_private
||
605 !MD
->isUserProvided())
609 // The seventh arg in populateFunctionInfo is a boolean passed by
610 // reference, its value is not relevant in here so it's not used
611 // anywhere besides the function call.
612 bool IsInAnonymousNamespace
;
613 populateFunctionInfo(FI
, MD
, /*FullComment=*/{}, /*LineNumber=*/{},
614 /*FileName=*/{}, IsFileInRootDir
,
615 IsInAnonymousNamespace
);
617 getFinalAccessSpecifier(BI
.Access
, MD
->getAccessUnsafe());
618 BI
.Children
.Functions
.emplace_back(std::move(FI
));
620 I
.Bases
.emplace_back(std::move(BI
));
621 // Call this function recursively to get the inherited classes of
622 // this base; these new bases will also get stored in the original
624 parseBases(I
, Base
, IsFileInRootDir
, PublicOnly
, false,
625 I
.Bases
.back().Access
);
631 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
632 emitInfo(const NamespaceDecl
*D
, const FullComment
*FC
, int LineNumber
,
633 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
634 auto I
= std::make_unique
<NamespaceInfo
>();
635 bool IsInAnonymousNamespace
= false;
636 populateInfo(*I
, D
, FC
, IsInAnonymousNamespace
);
637 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
640 I
->Name
= D
->isAnonymousNamespace()
641 ? llvm::SmallString
<16>("@nonymous_namespace")
643 I
->Path
= getInfoRelativePath(I
->Namespace
);
644 if (I
->Namespace
.empty() && I
->USR
== SymbolID())
645 return {std::unique_ptr
<Info
>{std::move(I
)}, nullptr};
647 // Namespaces are inserted into the parent by reference, so we need to return
648 // both the parent and the record itself.
649 return {std::move(I
), MakeAndInsertIntoParent
<const NamespaceInfo
&>(*I
)};
652 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
653 emitInfo(const RecordDecl
*D
, const FullComment
*FC
, int LineNumber
,
654 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
655 auto I
= std::make_unique
<RecordInfo
>();
656 bool IsInAnonymousNamespace
= false;
657 populateSymbolInfo(*I
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
658 IsInAnonymousNamespace
);
659 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
662 I
->TagType
= D
->getTagKind();
663 parseFields(*I
, D
, PublicOnly
);
664 if (const auto *C
= dyn_cast
<CXXRecordDecl
>(D
)) {
665 if (const TypedefNameDecl
*TD
= C
->getTypedefNameForAnonDecl()) {
666 I
->Name
= TD
->getNameAsString();
669 // TODO: remove first call to parseBases, that function should be deleted
671 parseBases(*I
, C
, IsFileInRootDir
, PublicOnly
, true);
673 I
->Path
= getInfoRelativePath(I
->Namespace
);
675 PopulateTemplateParameters(I
->Template
, D
);
677 // Full and partial specializations.
678 if (auto *CTSD
= dyn_cast
<ClassTemplateSpecializationDecl
>(D
)) {
680 I
->Template
.emplace();
681 I
->Template
->Specialization
.emplace();
682 auto &Specialization
= *I
->Template
->Specialization
;
684 // What this is a specialization of.
685 auto SpecOf
= CTSD
->getSpecializedTemplateOrPartial();
686 if (SpecOf
.is
<ClassTemplateDecl
*>()) {
687 Specialization
.SpecializationOf
=
688 getUSRForDecl(SpecOf
.get
<ClassTemplateDecl
*>());
689 } else if (SpecOf
.is
<ClassTemplatePartialSpecializationDecl
*>()) {
690 Specialization
.SpecializationOf
=
691 getUSRForDecl(SpecOf
.get
<ClassTemplatePartialSpecializationDecl
*>());
694 // Parameters to the specilization. For partial specializations, get the
695 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
696 // because the non-explicit template parameters will have generated internal
697 // placeholder names rather than the names the user typed that match the
698 // template parameters.
699 if (const ClassTemplatePartialSpecializationDecl
*CTPSD
=
700 dyn_cast
<ClassTemplatePartialSpecializationDecl
>(D
)) {
701 if (const ASTTemplateArgumentListInfo
*AsWritten
=
702 CTPSD
->getTemplateArgsAsWritten()) {
703 for (unsigned i
= 0; i
< AsWritten
->getNumTemplateArgs(); i
++) {
704 Specialization
.Params
.emplace_back(
705 getSourceCode(D
, (*AsWritten
)[i
].getSourceRange()));
709 for (const TemplateArgument
&Arg
: CTSD
->getTemplateArgs().asArray()) {
710 Specialization
.Params
.push_back(TemplateArgumentToInfo(D
, Arg
));
715 // Records are inserted into the parent by reference, so we need to return
716 // both the parent and the record itself.
717 auto Parent
= MakeAndInsertIntoParent
<const RecordInfo
&>(*I
);
718 return {std::move(I
), std::move(Parent
)};
721 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
722 emitInfo(const FunctionDecl
*D
, const FullComment
*FC
, int LineNumber
,
723 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
725 bool IsInAnonymousNamespace
= false;
726 populateFunctionInfo(Func
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
727 IsInAnonymousNamespace
);
728 Func
.Access
= clang::AccessSpecifier::AS_none
;
729 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
732 // Info is wrapped in its parent scope so is returned in the second position.
733 return {nullptr, MakeAndInsertIntoParent
<FunctionInfo
&&>(std::move(Func
))};
736 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
737 emitInfo(const CXXMethodDecl
*D
, const FullComment
*FC
, int LineNumber
,
738 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
740 bool IsInAnonymousNamespace
= false;
741 populateFunctionInfo(Func
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
742 IsInAnonymousNamespace
);
743 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
746 Func
.IsMethod
= true;
748 const NamedDecl
*Parent
= nullptr;
750 dyn_cast
<ClassTemplateSpecializationDecl
>(D
->getParent()))
751 Parent
= SD
->getSpecializedTemplate();
753 Parent
= D
->getParent();
755 SymbolID ParentUSR
= getUSRForDecl(Parent
);
757 Reference
{ParentUSR
, Parent
->getNameAsString(), InfoType::IT_record
,
758 Parent
->getQualifiedNameAsString()};
759 Func
.Access
= D
->getAccess();
761 // Info is wrapped in its parent scope so is returned in the second position.
762 return {nullptr, MakeAndInsertIntoParent
<FunctionInfo
&&>(std::move(Func
))};
765 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
766 emitInfo(const TypedefDecl
*D
, const FullComment
*FC
, int LineNumber
,
767 StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
770 bool IsInAnonymousNamespace
= false;
771 populateInfo(Info
, D
, FC
, IsInAnonymousNamespace
);
772 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
775 Info
.DefLoc
.emplace(LineNumber
, File
, IsFileInRootDir
);
776 Info
.Underlying
= getTypeInfoForType(D
->getUnderlyingType());
777 if (Info
.Underlying
.Type
.Name
.empty()) {
778 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
779 // The record serializer explicitly checks for this syntax and constructs
780 // a record with that name, so we don't want to emit a duplicate here.
783 Info
.IsUsing
= false;
785 // Info is wrapped in its parent scope so is returned in the second position.
786 return {nullptr, MakeAndInsertIntoParent
<TypedefInfo
&&>(std::move(Info
))};
789 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
790 // TypedefInfo with the IsUsing flag set.
791 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
792 emitInfo(const TypeAliasDecl
*D
, const FullComment
*FC
, int LineNumber
,
793 StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
796 bool IsInAnonymousNamespace
= false;
797 populateInfo(Info
, D
, FC
, IsInAnonymousNamespace
);
798 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
801 Info
.DefLoc
.emplace(LineNumber
, File
, IsFileInRootDir
);
802 Info
.Underlying
= getTypeInfoForType(D
->getUnderlyingType());
805 // Info is wrapped in its parent scope so is returned in the second position.
806 return {nullptr, MakeAndInsertIntoParent
<TypedefInfo
&&>(std::move(Info
))};
809 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
810 emitInfo(const EnumDecl
*D
, const FullComment
*FC
, int LineNumber
,
811 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
813 bool IsInAnonymousNamespace
= false;
814 populateSymbolInfo(Enum
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
815 IsInAnonymousNamespace
);
816 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
819 Enum
.Scoped
= D
->isScoped();
821 auto Name
= D
->getIntegerType().getAsString();
822 Enum
.BaseType
= TypeInfo(Name
, Name
);
824 parseEnumerators(Enum
, D
);
826 // Info is wrapped in its parent scope so is returned in the second position.
827 return {nullptr, MakeAndInsertIntoParent
<EnumInfo
&&>(std::move(Enum
))};
830 } // namespace serialize