blob: 54ee58960b22cd6fb1b62ba2024e50385326916e [file] [log] [blame]
Fangrui Songffe9f002019-03-01 09:52:53 +00001//===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===//
Julie Hockette975a472018-03-22 23:34:46 +00002//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Julie Hockette975a472018-03-22 23:34:46 +00006//
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
17using clang::comments::FullComment;
18
19namespace clang {
20namespace doc {
21namespace serialize {
22
23SymbolID hashUSR(llvm::StringRef USR) {
24 return llvm::SHA1::hash(arrayRefFromStringRef(USR));
25}
26
Julie Hockett2c1c9a22019-07-12 18:32:00 +000027template <typename T>
28static void
29populateParentNamespaces(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//
Kazuaki Ishizakib7ecf1c2020-01-04 10:28:41 -050035// Example: Given the below, the directory path for class C info will be
Julie Hockett2c1c9a22019-07-12 18:32:00 +000036// <root>/A/B
37//
38// namespace A {
39// namesapce B {
40//
41// class C {};
42//
43// }
44// }
45llvm::SmallString<128>
46getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
Julie Hockett2c1c9a22019-07-12 18:32:00 +000047 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
53llvm::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
Julie Hockette975a472018-03-22 23:34:46 +000063class ClangDocCommentVisitor
64 : public ConstCommentVisitor<ClangDocCommentVisitor> {
65public:
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
81private:
82 std::string getCommandName(unsigned CommandID) const;
83 bool isWhitespaceOnly(StringRef S) const;
84
85 CommentInfo &CurrentCI;
86};
87
88void 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())) {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +000093 CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
Julie Hockette975a472018-03-22 23:34:46 +000094 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
95 Visitor.parseComment(Child);
96 }
97}
98
99void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
100 if (!isWhitespaceOnly(C->getText()))
101 CurrentCI.Text = C->getText();
102}
103
104void 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
111void 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
122void ClangDocCommentVisitor::visitHTMLEndTagComment(
123 const HTMLEndTagComment *C) {
124 CurrentCI.Name = C->getTagName();
125 CurrentCI.SelfClosing = true;
126}
127
128void 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
135void 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
144void ClangDocCommentVisitor::visitTParamCommandComment(
145 const TParamCommandComment *C) {
146 if (C->hasParamName())
147 CurrentCI.ParamName = C->getParamNameAsWritten();
148}
149
150void ClangDocCommentVisitor::visitVerbatimBlockComment(
151 const VerbatimBlockComment *C) {
152 CurrentCI.Name = getCommandName(C->getCommandID());
153 CurrentCI.CloseName = C->getCloseName();
154}
155
156void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
157 const VerbatimBlockLineComment *C) {
158 if (!isWhitespaceOnly(C->getText()))
159 CurrentCI.Text = C->getText();
160}
161
162void ClangDocCommentVisitor::visitVerbatimLineComment(
163 const VerbatimLineComment *C) {
164 if (!isWhitespaceOnly(C->getText()))
165 CurrentCI.Text = C->getText();
166}
167
168bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
169 return std::all_of(S.begin(), S.end(), isspace);
170}
171
172std::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
182template <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
Julie Hockett8899c292018-08-02 20:10:17 +0000190std::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
Julie Hockette975a472018-03-22 23:34:46 +0000205static void parseFullComment(const FullComment *C, CommentInfo &CI) {
206 ClangDocCommentVisitor Visitor(CI);
207 Visitor.parseComment(C);
208}
209
210static 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
217static RecordDecl *getDeclForType(const QualType &T) {
Julie Hockettb1f01e22019-06-24 19:31:02 +0000218 if (const RecordDecl *D = T->getAsRecordDecl())
219 return D->getDefinition();
220 return nullptr;
Julie Hockette975a472018-03-22 23:34:46 +0000221}
222
Julie Hocketteb50a2e2018-07-20 18:49:55 +0000223static 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
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000233static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
234 const NamedDecl *D) {
235 bool IsAnonymousNamespace = false;
236 if (const auto *N = dyn_cast<NamespaceDecl>(D))
237 IsAnonymousNamespace = N->isAnonymousNamespace();
238 return !PublicOnly ||
239 (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
240 isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
241}
242
243// There are two uses for this function.
244// 1) Getting the resulting mode of inheritance of a record.
245// Example: class A {}; class B : private A {}; class C : public B {};
246// It's explicit that C is publicly inherited from C and B is privately
247// inherited from A. It's not explicit but C is also privately inherited from
248// A. This is the AS that this function calculates. FirstAS is the
249// inheritance mode of `class C : B` and SecondAS is the inheritance mode of
250// `class B : A`.
251// 2) Getting the inheritance mode of an inherited attribute / method.
252// Example : class A { public: int M; }; class B : private A {};
253// Class B is inherited from class A, which has a public attribute. This
254// attribute is now part of the derived class B but it's not public. This
255// will be private because the inheritance is private. This is the AS that
256// this function calculates. FirstAS is the inheritance mode and SecondAS is
257// the AS of the attribute / method.
258static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
259 AccessSpecifier SecondAS) {
260 if (FirstAS == AccessSpecifier::AS_none ||
261 SecondAS == AccessSpecifier::AS_none)
262 return AccessSpecifier::AS_none;
263 if (FirstAS == AccessSpecifier::AS_private ||
264 SecondAS == AccessSpecifier::AS_private)
265 return AccessSpecifier::AS_private;
266 if (FirstAS == AccessSpecifier::AS_protected ||
267 SecondAS == AccessSpecifier::AS_protected)
268 return AccessSpecifier::AS_protected;
269 return AccessSpecifier::AS_public;
270}
271
272// The Access parameter is only provided when parsing the field of an inherited
273// record, the access specification of the field depends on the inheritance mode
274static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
275 AccessSpecifier Access = AccessSpecifier::AS_public) {
Julie Hockette975a472018-03-22 23:34:46 +0000276 for (const FieldDecl *F : D->fields()) {
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000277 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
Julie Hocketteb50a2e2018-07-20 18:49:55 +0000278 continue;
Julie Hockette975a472018-03-22 23:34:46 +0000279 if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
Julie Hockettb59cd772018-05-04 17:02:13 +0000280 // 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)) {
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000283 I.Members.emplace_back(
284 getUSRForDecl(T), N->getNameAsString(), InfoType::IT_enum,
285 getInfoRelativePath(N), F->getNameAsString(),
286 getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
Julie Hockettb59cd772018-05-04 17:02:13 +0000287 continue;
288 } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000289 I.Members.emplace_back(
290 getUSRForDecl(T), N->getNameAsString(), InfoType::IT_record,
291 getInfoRelativePath(N), F->getNameAsString(),
292 getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
Julie Hockettb59cd772018-05-04 17:02:13 +0000293 continue;
294 }
Julie Hockette975a472018-03-22 23:34:46 +0000295 }
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000296 I.Members.emplace_back(
297 F->getTypeSourceInfo()->getType().getAsString(), F->getNameAsString(),
298 getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
Julie Hockette975a472018-03-22 23:34:46 +0000299 }
300}
301
302static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
303 for (const EnumConstantDecl *E : D->enumerators())
304 I.Members.emplace_back(E->getNameAsString());
305}
306
307static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
308 for (const ParmVarDecl *P : D->parameters()) {
Julie Hockette975a472018-03-22 23:34:46 +0000309 if (const auto *T = getDeclForType(P->getOriginalType())) {
Julie Hockettb59cd772018-05-04 17:02:13 +0000310 if (const auto *N = dyn_cast<EnumDecl>(T)) {
311 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000312 InfoType::IT_enum, getInfoRelativePath(N),
313 P->getNameAsString());
Julie Hockettb59cd772018-05-04 17:02:13 +0000314 continue;
315 } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
316 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000317 InfoType::IT_record, getInfoRelativePath(N),
318 P->getNameAsString());
Julie Hockettb59cd772018-05-04 17:02:13 +0000319 continue;
320 }
Julie Hockette975a472018-03-22 23:34:46 +0000321 }
Julie Hockettb59cd772018-05-04 17:02:13 +0000322 I.Params.emplace_back(P->getOriginalType().getAsString(),
323 P->getNameAsString());
Julie Hockette975a472018-03-22 23:34:46 +0000324 }
325}
326
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000327// TODO: Remove the serialization of Parents and VirtualParents, this
328// information is also extracted in the other definition of parseBases.
Julie Hockette975a472018-03-22 23:34:46 +0000329static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
Julie Hockett73a4d542018-10-03 18:25:27 +0000330 // Don't parse bases if this isn't a definition.
331 if (!D->isThisDeclarationADefinition())
332 return;
Julie Hockette975a472018-03-22 23:34:46 +0000333 for (const CXXBaseSpecifier &B : D->bases()) {
334 if (B.isVirtual())
335 continue;
Julie Hockettb1f01e22019-06-24 19:31:02 +0000336 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()))
Julie Hockettb59cd772018-05-04 17:02:13 +0000341 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000342 InfoType::IT_record, getInfoRelativePath(P));
Julie Hockette975a472018-03-22 23:34:46 +0000343 else
344 I.Parents.emplace_back(B.getType().getAsString());
345 }
346 for (const CXXBaseSpecifier &B : D->vbases()) {
347 if (const auto *P = getDeclForType(B.getType()))
Julie Hockettb59cd772018-05-04 17:02:13 +0000348 I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000349 InfoType::IT_record,
350 getInfoRelativePath(P));
Julie Hockette975a472018-03-22 23:34:46 +0000351 else
352 I.VirtualParents.emplace_back(B.getType().getAsString());
353 }
354}
355
356template <typename T>
357static void
358populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000359 const T *D, bool &IsInAnonymousNamespace) {
Julie Hockette975a472018-03-22 23:34:46 +0000360 const auto *DC = dyn_cast<DeclContext>(D);
361 while ((DC = DC->getParent())) {
Julie Hockettd900ef02019-06-28 19:07:56 +0000362 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
363 std::string Namespace;
364 if (N->isAnonymousNamespace()) {
365 Namespace = "@nonymous_namespace";
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000366 IsInAnonymousNamespace = true;
Julie Hockettd900ef02019-06-28 19:07:56 +0000367 } else
368 Namespace = N->getNameAsString();
369 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
Julie Hockettb59cd772018-05-04 17:02:13 +0000370 InfoType::IT_namespace);
Julie Hockettd900ef02019-06-28 19:07:56 +0000371 } else if (const auto *N = dyn_cast<RecordDecl>(DC))
Julie Hockettb59cd772018-05-04 17:02:13 +0000372 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
373 InfoType::IT_record);
Julie Hockette975a472018-03-22 23:34:46 +0000374 else if (const auto *N = dyn_cast<FunctionDecl>(DC))
Julie Hockettb59cd772018-05-04 17:02:13 +0000375 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
376 InfoType::IT_function);
Julie Hockette975a472018-03-22 23:34:46 +0000377 else if (const auto *N = dyn_cast<EnumDecl>(DC))
Julie Hockettb59cd772018-05-04 17:02:13 +0000378 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
379 InfoType::IT_enum);
Julie Hockette975a472018-03-22 23:34:46 +0000380 }
Diego Astiazaranb46131e2019-08-16 16:10:32 +0000381 // The global namespace should be added to the list of namespaces if the decl
382 // corresponds to a Record and if it doesn't have any namespace (because this
383 // means it's in the global namespace). Also if its outermost namespace is a
384 // record because that record matches the previous condition mentioned.
385 if ((Namespaces.empty() && dyn_cast<RecordDecl>(D)) ||
386 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
387 Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
388 InfoType::IT_namespace);
Julie Hockette975a472018-03-22 23:34:46 +0000389}
390
391template <typename T>
Julie Hockettd900ef02019-06-28 19:07:56 +0000392static void populateInfo(Info &I, const T *D, const FullComment *C,
393 bool &IsInAnonymousNamespace) {
Julie Hockette975a472018-03-22 23:34:46 +0000394 I.USR = getUSRForDecl(D);
395 I.Name = D->getNameAsString();
Julie Hockettd900ef02019-06-28 19:07:56 +0000396 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
Julie Hockette975a472018-03-22 23:34:46 +0000397 if (C) {
398 I.Description.emplace_back();
399 parseFullComment(C, I.Description.back());
400 }
401}
402
403template <typename T>
404static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
Julie Hockettd900ef02019-06-28 19:07:56 +0000405 int LineNumber, StringRef Filename,
Diego Astiazaran665e9672019-08-09 17:49:41 +0000406 bool IsFileInRootDir,
Julie Hockettd900ef02019-06-28 19:07:56 +0000407 bool &IsInAnonymousNamespace) {
408 populateInfo(I, D, C, IsInAnonymousNamespace);
Julie Hockette975a472018-03-22 23:34:46 +0000409 if (D->isThisDeclarationADefinition())
Diego Astiazaran665e9672019-08-09 17:49:41 +0000410 I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
Julie Hockette975a472018-03-22 23:34:46 +0000411 else
Diego Astiazaran665e9672019-08-09 17:49:41 +0000412 I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
Julie Hockette975a472018-03-22 23:34:46 +0000413}
414
415static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
416 const FullComment *FC, int LineNumber,
Diego Astiazaran665e9672019-08-09 17:49:41 +0000417 StringRef Filename, bool IsFileInRootDir,
Julie Hockettd900ef02019-06-28 19:07:56 +0000418 bool &IsInAnonymousNamespace) {
Diego Astiazaran665e9672019-08-09 17:49:41 +0000419 populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
420 IsInAnonymousNamespace);
Julie Hockette975a472018-03-22 23:34:46 +0000421 if (const auto *T = getDeclForType(D->getReturnType())) {
Julie Hockette975a472018-03-22 23:34:46 +0000422 if (dyn_cast<EnumDecl>(T))
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000423 I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
424 InfoType::IT_enum, getInfoRelativePath(T));
Julie Hockette975a472018-03-22 23:34:46 +0000425 else if (dyn_cast<RecordDecl>(T))
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000426 I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
427 InfoType::IT_record, getInfoRelativePath(T));
Julie Hockette975a472018-03-22 23:34:46 +0000428 } else {
Julie Hockettb59cd772018-05-04 17:02:13 +0000429 I.ReturnType = TypeInfo(D->getReturnType().getAsString());
Julie Hockette975a472018-03-22 23:34:46 +0000430 }
431 parseParameters(I, D);
432}
433
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000434static void
435parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
436 bool PublicOnly, bool IsParent,
437 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
438 // Don't parse bases if this isn't a definition.
439 if (!D->isThisDeclarationADefinition())
440 return;
441 for (const CXXBaseSpecifier &B : D->bases()) {
442 if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
443 if (const CXXRecordDecl *Base =
444 cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
445 // Initialized without USR and name, this will be set in the following
446 // if-else stmt.
447 BaseRecordInfo BI(
448 {}, "", getInfoRelativePath(Base), B.isVirtual(),
449 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
450 IsParent);
451 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
452 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
453 BI.USR = getUSRForDecl(D);
454 BI.Name = B.getType().getAsString();
455 } else {
456 BI.USR = getUSRForDecl(Base);
457 BI.Name = Base->getNameAsString();
458 }
459 parseFields(BI, Base, PublicOnly, BI.Access);
460 for (const auto &Decl : Base->decls())
461 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
462 // Don't serialize private methods
463 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
464 !MD->isUserProvided())
465 continue;
466 FunctionInfo FI;
467 FI.IsMethod = true;
468 // The seventh arg in populateFunctionInfo is a boolean passed by
469 // reference, its value is not relevant in here so it's not used
470 // anywhere besides the function call.
471 bool IsInAnonymousNamespace;
472 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
473 /*FileName=*/{}, IsFileInRootDir,
474 IsInAnonymousNamespace);
475 FI.Access =
476 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
477 BI.ChildFunctions.emplace_back(std::move(FI));
478 }
479 I.Bases.emplace_back(std::move(BI));
480 // Call this function recursively to get the inherited classes of
481 // this base; these new bases will also get stored in the original
482 // RecordInfo: I.
483 parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
484 I.Bases.back().Access);
485 }
486 }
487 }
488}
489
Julie Hockett097aedc2019-07-02 19:59:56 +0000490std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
491emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
Diego Astiazaran665e9672019-08-09 17:49:41 +0000492 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000493 auto I = std::make_unique<NamespaceInfo>();
Julie Hockettd900ef02019-06-28 19:07:56 +0000494 bool IsInAnonymousNamespace = false;
495 populateInfo(*I, D, FC, IsInAnonymousNamespace);
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000496 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
Julie Hockett097aedc2019-07-02 19:59:56 +0000497 return {};
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000498
Julie Hockettd900ef02019-06-28 19:07:56 +0000499 I->Name = D->isAnonymousNamespace()
500 ? llvm::SmallString<16>("@nonymous_namespace")
501 : I->Name;
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000502 I->Path = getInfoRelativePath(I->Namespace);
Julie Hockett097aedc2019-07-02 19:59:56 +0000503 if (I->Namespace.empty() && I->USR == SymbolID())
504 return {std::unique_ptr<Info>{std::move(I)}, nullptr};
505
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000506 auto ParentI = std::make_unique<NamespaceInfo>();
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000507 ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
Diego Astiazarane27f7782019-08-12 18:42:46 +0000508 ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace,
509 getInfoRelativePath(I->Namespace));
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000510 if (I->Namespace.empty())
511 ParentI->Path = getInfoRelativePath(ParentI->Namespace);
Julie Hockett097aedc2019-07-02 19:59:56 +0000512 return {std::unique_ptr<Info>{std::move(I)},
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000513 std::unique_ptr<Info>{std::move(ParentI)}};
Julie Hockette975a472018-03-22 23:34:46 +0000514}
515
Julie Hockett097aedc2019-07-02 19:59:56 +0000516std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
517emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
Diego Astiazaran665e9672019-08-09 17:49:41 +0000518 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000519 auto I = std::make_unique<RecordInfo>();
Julie Hockettd900ef02019-06-28 19:07:56 +0000520 bool IsInAnonymousNamespace = false;
Diego Astiazaran665e9672019-08-09 17:49:41 +0000521 populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
522 IsInAnonymousNamespace);
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000523 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
Julie Hockett097aedc2019-07-02 19:59:56 +0000524 return {};
Julie Hockettd900ef02019-06-28 19:07:56 +0000525
Julie Hockett8899c292018-08-02 20:10:17 +0000526 I->TagType = D->getTagKind();
527 parseFields(*I, D, PublicOnly);
Julie Hockettb1f01e22019-06-24 19:31:02 +0000528 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
529 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
530 I->Name = TD->getNameAsString();
531 I->IsTypeDef = true;
532 }
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000533 // TODO: remove first call to parseBases, that function should be deleted
Julie Hockett8899c292018-08-02 20:10:17 +0000534 parseBases(*I, C);
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000535 parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
Julie Hockettb1f01e22019-06-24 19:31:02 +0000536 }
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000537 I->Path = getInfoRelativePath(I->Namespace);
Julie Hockett097aedc2019-07-02 19:59:56 +0000538
Julie Hockett097aedc2019-07-02 19:59:56 +0000539 switch (I->Namespace[0].RefType) {
540 case InfoType::IT_namespace: {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000541 auto ParentI = std::make_unique<NamespaceInfo>();
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000542 ParentI->USR = I->Namespace[0].USR;
Diego Astiazarane27f7782019-08-12 18:42:46 +0000543 ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
544 getInfoRelativePath(I->Namespace));
Julie Hockett097aedc2019-07-02 19:59:56 +0000545 return {std::unique_ptr<Info>{std::move(I)},
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000546 std::unique_ptr<Info>{std::move(ParentI)}};
Julie Hockett097aedc2019-07-02 19:59:56 +0000547 }
548 case InfoType::IT_record: {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000549 auto ParentI = std::make_unique<RecordInfo>();
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000550 ParentI->USR = I->Namespace[0].USR;
Diego Astiazarane27f7782019-08-12 18:42:46 +0000551 ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
552 getInfoRelativePath(I->Namespace));
Julie Hockett097aedc2019-07-02 19:59:56 +0000553 return {std::unique_ptr<Info>{std::move(I)},
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000554 std::unique_ptr<Info>{std::move(ParentI)}};
Julie Hockett097aedc2019-07-02 19:59:56 +0000555 }
556 default:
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000557 llvm_unreachable("Invalid reference type for parent namespace");
Julie Hockett097aedc2019-07-02 19:59:56 +0000558 }
Julie Hockette975a472018-03-22 23:34:46 +0000559}
560
Julie Hockett097aedc2019-07-02 19:59:56 +0000561std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
562emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
Diego Astiazaran665e9672019-08-09 17:49:41 +0000563 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
Julie Hockett8899c292018-08-02 20:10:17 +0000564 FunctionInfo Func;
Julie Hockettd900ef02019-06-28 19:07:56 +0000565 bool IsInAnonymousNamespace = false;
Diego Astiazaran665e9672019-08-09 17:49:41 +0000566 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
567 IsInAnonymousNamespace);
Diego Astiazaran6a29ae42019-08-15 23:04:27 +0000568 Func.Access = clang::AccessSpecifier::AS_none;
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000569 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
Julie Hockett097aedc2019-07-02 19:59:56 +0000570 return {};
Julie Hockettd900ef02019-06-28 19:07:56 +0000571
Julie Hockett8899c292018-08-02 20:10:17 +0000572 // Wrap in enclosing scope
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000573 auto ParentI = std::make_unique<NamespaceInfo>();
Julie Hockett8899c292018-08-02 20:10:17 +0000574 if (!Func.Namespace.empty())
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000575 ParentI->USR = Func.Namespace[0].USR;
Julie Hockett8899c292018-08-02 20:10:17 +0000576 else
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000577 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)}};
Julie Hockette975a472018-03-22 23:34:46 +0000583}
584
Julie Hockett097aedc2019-07-02 19:59:56 +0000585std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
586emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
Diego Astiazaran665e9672019-08-09 17:49:41 +0000587 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
Julie Hockett8899c292018-08-02 20:10:17 +0000588 FunctionInfo Func;
Julie Hockettd900ef02019-06-28 19:07:56 +0000589 bool IsInAnonymousNamespace = false;
Diego Astiazaran665e9672019-08-09 17:49:41 +0000590 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
591 IsInAnonymousNamespace);
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000592 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
Julie Hockett097aedc2019-07-02 19:59:56 +0000593 return {};
Julie Hockettd900ef02019-06-28 19:07:56 +0000594
Julie Hockett8899c292018-08-02 20:10:17 +0000595 Func.IsMethod = true;
596
Julie Hockettb1f01e22019-06-24 19:31:02 +0000597 const NamedDecl *Parent = nullptr;
598 if (const auto *SD =
599 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
600 Parent = SD->getSpecializedTemplate();
601 else
602 Parent = D->getParent();
603
604 SymbolID ParentUSR = getUSRForDecl(Parent);
605 Func.Parent =
606 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record};
Julie Hockett8899c292018-08-02 20:10:17 +0000607 Func.Access = D->getAccess();
608
609 // Wrap in enclosing scope
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000610 auto ParentI = std::make_unique<RecordInfo>();
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000611 ParentI->USR = ParentUSR;
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000612 ParentI->ChildFunctions.emplace_back(std::move(Func));
Julie Hockett097aedc2019-07-02 19:59:56 +0000613 // Info is wrapped in its parent scope so it's returned in the second position
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000614 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
Julie Hockette975a472018-03-22 23:34:46 +0000615}
616
Julie Hockett097aedc2019-07-02 19:59:56 +0000617std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
618emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
Diego Astiazaran665e9672019-08-09 17:49:41 +0000619 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
Julie Hockett8899c292018-08-02 20:10:17 +0000620 EnumInfo Enum;
Julie Hockettd900ef02019-06-28 19:07:56 +0000621 bool IsInAnonymousNamespace = false;
Diego Astiazaran665e9672019-08-09 17:49:41 +0000622 populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
623 IsInAnonymousNamespace);
Diego Astiazaranba3d5952019-08-16 00:10:49 +0000624 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
Julie Hockett097aedc2019-07-02 19:59:56 +0000625 return {};
Julie Hockettd900ef02019-06-28 19:07:56 +0000626
Julie Hockett8899c292018-08-02 20:10:17 +0000627 Enum.Scoped = D->isScoped();
628 parseEnumerators(Enum, D);
629
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000630 // Put in global namespace
631 if (Enum.Namespace.empty()) {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000632 auto ParentI = std::make_unique<NamespaceInfo>();
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000633 ParentI->USR = SymbolID();
634 ParentI->ChildEnums.emplace_back(std::move(Enum));
635 ParentI->Path = getInfoRelativePath(ParentI->Namespace);
636 // Info is wrapped in its parent scope so it's returned in the second
637 // position
638 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
Julie Hockett8899c292018-08-02 20:10:17 +0000639 }
640
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000641 // Wrap in enclosing scope
642 switch (Enum.Namespace[0].RefType) {
643 case InfoType::IT_namespace: {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000644 auto ParentI = std::make_unique<NamespaceInfo>();
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000645 ParentI->USR = Enum.Namespace[0].USR;
646 ParentI->ChildEnums.emplace_back(std::move(Enum));
647 // Info is wrapped in its parent scope so it's returned in the second
648 // position
649 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
650 }
651 case InfoType::IT_record: {
Jonas Devlieghere1c705d92019-08-14 23:52:23 +0000652 auto ParentI = std::make_unique<RecordInfo>();
Julie Hockett2c1c9a22019-07-12 18:32:00 +0000653 ParentI->USR = Enum.Namespace[0].USR;
654 ParentI->ChildEnums.emplace_back(std::move(Enum));
655 // Info is wrapped in its parent scope so it's returned in the second
656 // position
657 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
658 }
659 default:
660 llvm_unreachable("Invalid reference type for parent namespace");
661 }
Julie Hockette975a472018-03-22 23:34:46 +0000662}
663
664} // namespace serialize
665} // namespace doc
666} // namespace clang