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
, const PrintingPolicy
&Policy
) {
240 const TagDecl
*TD
= getTagDeclForType(T
);
242 return TypeInfo(Reference(SymbolID(), T
.getAsString(Policy
)));
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(Policy
), 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 auto &LO
= F
->getLangOpts();
383 // Use getAccessUnsafe so that we just get the default AS_none if it's not
384 // valid, as opposed to an assert.
385 MemberTypeInfo
&NewMember
= I
.Members
.emplace_back(
386 getTypeInfoForType(F
->getTypeSourceInfo()->getType(), LO
),
387 F
->getNameAsString(),
388 getFinalAccessSpecifier(Access
, F
->getAccessUnsafe()));
389 populateMemberTypeInfo(NewMember
, F
);
393 static void parseEnumerators(EnumInfo
&I
, const EnumDecl
*D
) {
394 for (const EnumConstantDecl
*E
: D
->enumerators()) {
395 std::string ValueExpr
;
396 if (const Expr
*InitExpr
= E
->getInitExpr())
397 ValueExpr
= getSourceCode(D
, InitExpr
->getSourceRange());
398 SmallString
<16> ValueStr
;
399 E
->getInitVal().toString(ValueStr
);
400 I
.Members
.emplace_back(E
->getNameAsString(), ValueStr
.str(), ValueExpr
);
401 ASTContext
&Context
= E
->getASTContext();
402 if (RawComment
*Comment
=
403 E
->getASTContext().getRawCommentForDeclNoCache(E
)) {
405 Comment
->setAttached();
406 if (comments::FullComment
*Fc
= Comment
->parse(Context
, nullptr, E
)) {
407 EnumValueInfo
&Member
= I
.Members
.back();
408 Member
.Description
.emplace_back();
409 parseFullComment(Fc
, Member
.Description
.back());
415 static void parseParameters(FunctionInfo
&I
, const FunctionDecl
*D
) {
416 auto &LO
= D
->getLangOpts();
417 for (const ParmVarDecl
*P
: D
->parameters()) {
418 FieldTypeInfo
&FieldInfo
= I
.Params
.emplace_back(
419 getTypeInfoForType(P
->getOriginalType(), LO
), P
->getNameAsString());
420 FieldInfo
.DefaultValue
= getSourceCode(D
, P
->getDefaultArgRange());
424 // TODO: Remove the serialization of Parents and VirtualParents, this
425 // information is also extracted in the other definition of parseBases.
426 static void parseBases(RecordInfo
&I
, const CXXRecordDecl
*D
) {
427 // Don't parse bases if this isn't a definition.
428 if (!D
->isThisDeclarationADefinition())
430 for (const CXXBaseSpecifier
&B
: D
->bases()) {
433 if (const auto *Ty
= B
.getType()->getAs
<TemplateSpecializationType
>()) {
434 const TemplateDecl
*D
= Ty
->getTemplateName().getAsTemplateDecl();
435 I
.Parents
.emplace_back(getUSRForDecl(D
), B
.getType().getAsString(),
436 InfoType::IT_record
, B
.getType().getAsString());
437 } else if (const RecordDecl
*P
= getRecordDeclForType(B
.getType()))
438 I
.Parents
.emplace_back(getUSRForDecl(P
), P
->getNameAsString(),
439 InfoType::IT_record
, P
->getQualifiedNameAsString(),
440 getInfoRelativePath(P
));
442 I
.Parents
.emplace_back(SymbolID(), B
.getType().getAsString());
444 for (const CXXBaseSpecifier
&B
: D
->vbases()) {
445 if (const RecordDecl
*P
= getRecordDeclForType(B
.getType()))
446 I
.VirtualParents
.emplace_back(
447 getUSRForDecl(P
), P
->getNameAsString(), InfoType::IT_record
,
448 P
->getQualifiedNameAsString(), getInfoRelativePath(P
));
450 I
.VirtualParents
.emplace_back(SymbolID(), B
.getType().getAsString());
454 template <typename T
>
456 populateParentNamespaces(llvm::SmallVector
<Reference
, 4> &Namespaces
,
457 const T
*D
, bool &IsInAnonymousNamespace
) {
458 const DeclContext
*DC
= D
->getDeclContext();
460 if (const auto *N
= dyn_cast
<NamespaceDecl
>(DC
)) {
461 std::string Namespace
;
462 if (N
->isAnonymousNamespace()) {
463 Namespace
= "@nonymous_namespace";
464 IsInAnonymousNamespace
= true;
466 Namespace
= N
->getNameAsString();
467 Namespaces
.emplace_back(getUSRForDecl(N
), Namespace
,
468 InfoType::IT_namespace
,
469 N
->getQualifiedNameAsString());
470 } else if (const auto *N
= dyn_cast
<RecordDecl
>(DC
))
471 Namespaces
.emplace_back(getUSRForDecl(N
), N
->getNameAsString(),
473 N
->getQualifiedNameAsString());
474 else if (const auto *N
= dyn_cast
<FunctionDecl
>(DC
))
475 Namespaces
.emplace_back(getUSRForDecl(N
), N
->getNameAsString(),
476 InfoType::IT_function
,
477 N
->getQualifiedNameAsString());
478 else if (const auto *N
= dyn_cast
<EnumDecl
>(DC
))
479 Namespaces
.emplace_back(getUSRForDecl(N
), N
->getNameAsString(),
480 InfoType::IT_enum
, N
->getQualifiedNameAsString());
481 } while ((DC
= DC
->getParent()));
482 // The global namespace should be added to the list of namespaces if the decl
483 // corresponds to a Record and if it doesn't have any namespace (because this
484 // means it's in the global namespace). Also if its outermost namespace is a
485 // record because that record matches the previous condition mentioned.
486 if ((Namespaces
.empty() && isa
<RecordDecl
>(D
)) ||
487 (!Namespaces
.empty() && Namespaces
.back().RefType
== InfoType::IT_record
))
488 Namespaces
.emplace_back(SymbolID(), "GlobalNamespace",
489 InfoType::IT_namespace
);
492 void PopulateTemplateParameters(std::optional
<TemplateInfo
> &TemplateInfo
,
493 const clang::Decl
*D
) {
494 if (const TemplateParameterList
*ParamList
=
495 D
->getDescribedTemplateParams()) {
497 TemplateInfo
.emplace();
499 for (const NamedDecl
*ND
: *ParamList
) {
500 TemplateInfo
->Params
.emplace_back(
501 getSourceCode(ND
, ND
->getSourceRange()));
506 TemplateParamInfo
TemplateArgumentToInfo(const clang::Decl
*D
,
507 const TemplateArgument
&Arg
) {
508 // The TemplateArgument's pretty printing handles all the normal cases
509 // well enough for our requirements.
511 llvm::raw_string_ostream
Stream(Str
);
512 Arg
.print(PrintingPolicy(D
->getLangOpts()), Stream
, false);
513 return TemplateParamInfo(Str
);
516 template <typename T
>
517 static void populateInfo(Info
&I
, const T
*D
, const FullComment
*C
,
518 bool &IsInAnonymousNamespace
) {
519 I
.USR
= getUSRForDecl(D
);
520 I
.Name
= D
->getNameAsString();
521 populateParentNamespaces(I
.Namespace
, D
, IsInAnonymousNamespace
);
523 I
.Description
.emplace_back();
524 parseFullComment(C
, I
.Description
.back());
528 template <typename T
>
529 static void populateSymbolInfo(SymbolInfo
&I
, const T
*D
, const FullComment
*C
,
530 int LineNumber
, StringRef Filename
,
531 bool IsFileInRootDir
,
532 bool &IsInAnonymousNamespace
) {
533 populateInfo(I
, D
, C
, IsInAnonymousNamespace
);
534 if (D
->isThisDeclarationADefinition())
535 I
.DefLoc
.emplace(LineNumber
, Filename
, IsFileInRootDir
);
537 I
.Loc
.emplace_back(LineNumber
, Filename
, IsFileInRootDir
);
540 static void populateFunctionInfo(FunctionInfo
&I
, const FunctionDecl
*D
,
541 const FullComment
*FC
, int LineNumber
,
542 StringRef Filename
, bool IsFileInRootDir
,
543 bool &IsInAnonymousNamespace
) {
544 populateSymbolInfo(I
, D
, FC
, LineNumber
, Filename
, IsFileInRootDir
,
545 IsInAnonymousNamespace
);
546 auto &LO
= D
->getLangOpts();
547 I
.ReturnType
= getTypeInfoForType(D
->getReturnType(), LO
);
548 parseParameters(I
, D
);
550 PopulateTemplateParameters(I
.Template
, D
);
552 // Handle function template specializations.
553 if (const FunctionTemplateSpecializationInfo
*FTSI
=
554 D
->getTemplateSpecializationInfo()) {
556 I
.Template
.emplace();
557 I
.Template
->Specialization
.emplace();
558 auto &Specialization
= *I
.Template
->Specialization
;
560 Specialization
.SpecializationOf
= getUSRForDecl(FTSI
->getTemplate());
562 // Template parameters to the specialization.
563 if (FTSI
->TemplateArguments
) {
564 for (const TemplateArgument
&Arg
: FTSI
->TemplateArguments
->asArray()) {
565 Specialization
.Params
.push_back(TemplateArgumentToInfo(D
, Arg
));
571 static void populateMemberTypeInfo(MemberTypeInfo
&I
, const FieldDecl
*D
) {
572 assert(D
&& "Expect non-null FieldDecl in populateMemberTypeInfo");
574 ASTContext
& Context
= D
->getASTContext();
575 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
576 // of this logic. See also similar code in Mapper.cpp.
577 RawComment
*Comment
= Context
.getRawCommentForDeclNoCache(D
);
581 Comment
->setAttached();
582 if (comments::FullComment
*fc
= Comment
->parse(Context
, nullptr, D
)) {
583 I
.Description
.emplace_back();
584 parseFullComment(fc
, I
.Description
.back());
589 parseBases(RecordInfo
&I
, const CXXRecordDecl
*D
, bool IsFileInRootDir
,
590 bool PublicOnly
, bool IsParent
,
591 AccessSpecifier ParentAccess
= AccessSpecifier::AS_public
) {
592 // Don't parse bases if this isn't a definition.
593 if (!D
->isThisDeclarationADefinition())
595 for (const CXXBaseSpecifier
&B
: D
->bases()) {
596 if (const RecordType
*Ty
= B
.getType()->getAs
<RecordType
>()) {
597 if (const CXXRecordDecl
*Base
=
598 cast_or_null
<CXXRecordDecl
>(Ty
->getDecl()->getDefinition())) {
599 // Initialized without USR and name, this will be set in the following
602 {}, "", getInfoRelativePath(Base
), B
.isVirtual(),
603 getFinalAccessSpecifier(ParentAccess
, B
.getAccessSpecifier()),
605 if (const auto *Ty
= B
.getType()->getAs
<TemplateSpecializationType
>()) {
606 const TemplateDecl
*D
= Ty
->getTemplateName().getAsTemplateDecl();
607 BI
.USR
= getUSRForDecl(D
);
608 BI
.Name
= B
.getType().getAsString();
610 BI
.USR
= getUSRForDecl(Base
);
611 BI
.Name
= Base
->getNameAsString();
613 parseFields(BI
, Base
, PublicOnly
, BI
.Access
);
614 for (const auto &Decl
: Base
->decls())
615 if (const auto *MD
= dyn_cast
<CXXMethodDecl
>(Decl
)) {
616 // Don't serialize private methods
617 if (MD
->getAccessUnsafe() == AccessSpecifier::AS_private
||
618 !MD
->isUserProvided())
622 // The seventh arg in populateFunctionInfo is a boolean passed by
623 // reference, its value is not relevant in here so it's not used
624 // anywhere besides the function call.
625 bool IsInAnonymousNamespace
;
626 populateFunctionInfo(FI
, MD
, /*FullComment=*/{}, /*LineNumber=*/{},
627 /*FileName=*/{}, IsFileInRootDir
,
628 IsInAnonymousNamespace
);
630 getFinalAccessSpecifier(BI
.Access
, MD
->getAccessUnsafe());
631 BI
.Children
.Functions
.emplace_back(std::move(FI
));
633 I
.Bases
.emplace_back(std::move(BI
));
634 // Call this function recursively to get the inherited classes of
635 // this base; these new bases will also get stored in the original
637 parseBases(I
, Base
, IsFileInRootDir
, PublicOnly
, false,
638 I
.Bases
.back().Access
);
644 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
645 emitInfo(const NamespaceDecl
*D
, const FullComment
*FC
, int LineNumber
,
646 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
647 auto I
= std::make_unique
<NamespaceInfo
>();
648 bool IsInAnonymousNamespace
= false;
649 populateInfo(*I
, D
, FC
, IsInAnonymousNamespace
);
650 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
653 I
->Name
= D
->isAnonymousNamespace()
654 ? llvm::SmallString
<16>("@nonymous_namespace")
656 I
->Path
= getInfoRelativePath(I
->Namespace
);
657 if (I
->Namespace
.empty() && I
->USR
== SymbolID())
658 return {std::unique_ptr
<Info
>{std::move(I
)}, nullptr};
660 // Namespaces are inserted into the parent by reference, so we need to return
661 // both the parent and the record itself.
662 return {std::move(I
), MakeAndInsertIntoParent
<const NamespaceInfo
&>(*I
)};
665 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
666 emitInfo(const RecordDecl
*D
, const FullComment
*FC
, int LineNumber
,
667 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
668 auto I
= std::make_unique
<RecordInfo
>();
669 bool IsInAnonymousNamespace
= false;
670 populateSymbolInfo(*I
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
671 IsInAnonymousNamespace
);
672 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
675 I
->TagType
= D
->getTagKind();
676 parseFields(*I
, D
, PublicOnly
);
677 if (const auto *C
= dyn_cast
<CXXRecordDecl
>(D
)) {
678 if (const TypedefNameDecl
*TD
= C
->getTypedefNameForAnonDecl()) {
679 I
->Name
= TD
->getNameAsString();
682 // TODO: remove first call to parseBases, that function should be deleted
684 parseBases(*I
, C
, IsFileInRootDir
, PublicOnly
, true);
686 I
->Path
= getInfoRelativePath(I
->Namespace
);
688 PopulateTemplateParameters(I
->Template
, D
);
690 // Full and partial specializations.
691 if (auto *CTSD
= dyn_cast
<ClassTemplateSpecializationDecl
>(D
)) {
693 I
->Template
.emplace();
694 I
->Template
->Specialization
.emplace();
695 auto &Specialization
= *I
->Template
->Specialization
;
697 // What this is a specialization of.
698 auto SpecOf
= CTSD
->getSpecializedTemplateOrPartial();
699 if (auto *CTD
= dyn_cast
<ClassTemplateDecl
*>(SpecOf
))
700 Specialization
.SpecializationOf
= getUSRForDecl(CTD
);
701 else if (auto *CTPSD
=
702 dyn_cast
<ClassTemplatePartialSpecializationDecl
*>(SpecOf
))
703 Specialization
.SpecializationOf
= getUSRForDecl(CTPSD
);
705 // Parameters to the specilization. For partial specializations, get the
706 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
707 // because the non-explicit template parameters will have generated internal
708 // placeholder names rather than the names the user typed that match the
709 // template parameters.
710 if (const ClassTemplatePartialSpecializationDecl
*CTPSD
=
711 dyn_cast
<ClassTemplatePartialSpecializationDecl
>(D
)) {
712 if (const ASTTemplateArgumentListInfo
*AsWritten
=
713 CTPSD
->getTemplateArgsAsWritten()) {
714 for (unsigned i
= 0; i
< AsWritten
->getNumTemplateArgs(); i
++) {
715 Specialization
.Params
.emplace_back(
716 getSourceCode(D
, (*AsWritten
)[i
].getSourceRange()));
720 for (const TemplateArgument
&Arg
: CTSD
->getTemplateArgs().asArray()) {
721 Specialization
.Params
.push_back(TemplateArgumentToInfo(D
, Arg
));
726 // Records are inserted into the parent by reference, so we need to return
727 // both the parent and the record itself.
728 auto Parent
= MakeAndInsertIntoParent
<const RecordInfo
&>(*I
);
729 return {std::move(I
), std::move(Parent
)};
732 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
733 emitInfo(const FunctionDecl
*D
, const FullComment
*FC
, int LineNumber
,
734 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
736 bool IsInAnonymousNamespace
= false;
737 populateFunctionInfo(Func
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
738 IsInAnonymousNamespace
);
739 Func
.Access
= clang::AccessSpecifier::AS_none
;
740 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
743 // Info is wrapped in its parent scope so is returned in the second position.
744 return {nullptr, MakeAndInsertIntoParent
<FunctionInfo
&&>(std::move(Func
))};
747 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
748 emitInfo(const CXXMethodDecl
*D
, const FullComment
*FC
, int LineNumber
,
749 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
751 bool IsInAnonymousNamespace
= false;
752 populateFunctionInfo(Func
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
753 IsInAnonymousNamespace
);
754 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
757 Func
.IsMethod
= true;
759 const NamedDecl
*Parent
= nullptr;
761 dyn_cast
<ClassTemplateSpecializationDecl
>(D
->getParent()))
762 Parent
= SD
->getSpecializedTemplate();
764 Parent
= D
->getParent();
766 SymbolID ParentUSR
= getUSRForDecl(Parent
);
768 Reference
{ParentUSR
, Parent
->getNameAsString(), InfoType::IT_record
,
769 Parent
->getQualifiedNameAsString()};
770 Func
.Access
= D
->getAccess();
772 // Info is wrapped in its parent scope so is returned in the second position.
773 return {nullptr, MakeAndInsertIntoParent
<FunctionInfo
&&>(std::move(Func
))};
776 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
777 emitInfo(const TypedefDecl
*D
, const FullComment
*FC
, int LineNumber
,
778 StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
781 bool IsInAnonymousNamespace
= false;
782 populateInfo(Info
, D
, FC
, IsInAnonymousNamespace
);
783 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
786 Info
.DefLoc
.emplace(LineNumber
, File
, IsFileInRootDir
);
787 auto &LO
= D
->getLangOpts();
788 Info
.Underlying
= getTypeInfoForType(D
->getUnderlyingType(), LO
);
789 if (Info
.Underlying
.Type
.Name
.empty()) {
790 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
791 // The record serializer explicitly checks for this syntax and constructs
792 // a record with that name, so we don't want to emit a duplicate here.
795 Info
.IsUsing
= false;
797 // Info is wrapped in its parent scope so is returned in the second position.
798 return {nullptr, MakeAndInsertIntoParent
<TypedefInfo
&&>(std::move(Info
))};
801 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
802 // TypedefInfo with the IsUsing flag set.
803 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
804 emitInfo(const TypeAliasDecl
*D
, const FullComment
*FC
, int LineNumber
,
805 StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
808 bool IsInAnonymousNamespace
= false;
809 populateInfo(Info
, D
, FC
, IsInAnonymousNamespace
);
810 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
813 Info
.DefLoc
.emplace(LineNumber
, File
, IsFileInRootDir
);
814 auto &LO
= D
->getLangOpts();
815 Info
.Underlying
= getTypeInfoForType(D
->getUnderlyingType(), LO
);
818 // Info is wrapped in its parent scope so is returned in the second position.
819 return {nullptr, MakeAndInsertIntoParent
<TypedefInfo
&&>(std::move(Info
))};
822 std::pair
<std::unique_ptr
<Info
>, std::unique_ptr
<Info
>>
823 emitInfo(const EnumDecl
*D
, const FullComment
*FC
, int LineNumber
,
824 llvm::StringRef File
, bool IsFileInRootDir
, bool PublicOnly
) {
826 bool IsInAnonymousNamespace
= false;
827 populateSymbolInfo(Enum
, D
, FC
, LineNumber
, File
, IsFileInRootDir
,
828 IsInAnonymousNamespace
);
829 if (!shouldSerializeInfo(PublicOnly
, IsInAnonymousNamespace
, D
))
832 Enum
.Scoped
= D
->isScoped();
834 auto Name
= D
->getIntegerType().getAsString();
835 Enum
.BaseType
= TypeInfo(Name
, Name
);
837 parseEnumerators(Enum
, D
);
839 // Info is wrapped in its parent scope so is returned in the second position.
840 return {nullptr, MakeAndInsertIntoParent
<EnumInfo
&&>(std::move(Enum
))};
843 } // namespace serialize