150
|
1 //===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===//
|
|
2 //
|
|
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
4 // See https://llvm.org/LICENSE.txt for license information.
|
|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
6 //
|
|
7 //===----------------------------------------------------------------------===//
|
|
8
|
|
9 #include "Serialize.h"
|
|
10 #include "BitcodeWriter.h"
|
|
11 #include "clang/AST/Comment.h"
|
|
12 #include "clang/Index/USRGeneration.h"
|
|
13 #include "llvm/ADT/Hashing.h"
|
|
14 #include "llvm/ADT/StringExtras.h"
|
|
15 #include "llvm/Support/SHA1.h"
|
|
16
|
|
17 using clang::comments::FullComment;
|
|
18
|
|
19 namespace clang {
|
|
20 namespace doc {
|
|
21 namespace serialize {
|
|
22
|
|
23 SymbolID hashUSR(llvm::StringRef USR) {
|
|
24 return llvm::SHA1::hash(arrayRefFromStringRef(USR));
|
|
25 }
|
|
26
|
|
27 template <typename T>
|
|
28 static void
|
|
29 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
|
|
30 const T *D, bool &IsAnonymousNamespace);
|
|
31
|
|
32 // A function to extract the appropriate relative path for a given info's
|
|
33 // documentation. The path returned is a composite of the parent namespaces.
|
|
34 //
|
|
35 // Example: Given the below, the directory path for class C info will be
|
|
36 // <root>/A/B
|
|
37 //
|
|
38 // namespace A {
|
173
|
39 // namespace B {
|
150
|
40 //
|
|
41 // class C {};
|
|
42 //
|
|
43 // }
|
|
44 // }
|
|
45 llvm::SmallString<128>
|
|
46 getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
|
|
47 llvm::SmallString<128> Path;
|
|
48 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
|
|
49 llvm::sys::path::append(Path, R->Name);
|
|
50 return Path;
|
|
51 }
|
|
52
|
|
53 llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
|
|
54 llvm::SmallVector<Reference, 4> Namespaces;
|
|
55 // The third arg in populateParentNamespaces is a boolean passed by reference,
|
|
56 // its value is not relevant in here so it's not used anywhere besides the
|
|
57 // function call
|
|
58 bool B = true;
|
|
59 populateParentNamespaces(Namespaces, D, B);
|
|
60 return getInfoRelativePath(Namespaces);
|
|
61 }
|
|
62
|
|
63 class ClangDocCommentVisitor
|
|
64 : public ConstCommentVisitor<ClangDocCommentVisitor> {
|
|
65 public:
|
|
66 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
|
|
67
|
|
68 void parseComment(const comments::Comment *C);
|
|
69
|
|
70 void visitTextComment(const TextComment *C);
|
|
71 void visitInlineCommandComment(const InlineCommandComment *C);
|
|
72 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
|
|
73 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
|
|
74 void visitBlockCommandComment(const BlockCommandComment *C);
|
|
75 void visitParamCommandComment(const ParamCommandComment *C);
|
|
76 void visitTParamCommandComment(const TParamCommandComment *C);
|
|
77 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
|
|
78 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
|
|
79 void visitVerbatimLineComment(const VerbatimLineComment *C);
|
|
80
|
|
81 private:
|
|
82 std::string getCommandName(unsigned CommandID) const;
|
|
83 bool isWhitespaceOnly(StringRef S) const;
|
|
84
|
|
85 CommentInfo &CurrentCI;
|
|
86 };
|
|
87
|
|
88 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
|
|
89 CurrentCI.Kind = C->getCommentKindName();
|
|
90 ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
|
|
91 for (comments::Comment *Child :
|
|
92 llvm::make_range(C->child_begin(), C->child_end())) {
|
|
93 CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
|
|
94 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
|
|
95 Visitor.parseComment(Child);
|
|
96 }
|
|
97 }
|
|
98
|
|
99 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
|
|
100 if (!isWhitespaceOnly(C->getText()))
|
|
101 CurrentCI.Text = C->getText();
|
|
102 }
|
|
103
|
|
104 void ClangDocCommentVisitor::visitInlineCommandComment(
|
|
105 const InlineCommandComment *C) {
|
|
106 CurrentCI.Name = getCommandName(C->getCommandID());
|
|
107 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
|
|
108 CurrentCI.Args.push_back(C->getArgText(I));
|
|
109 }
|
|
110
|
|
111 void ClangDocCommentVisitor::visitHTMLStartTagComment(
|
|
112 const HTMLStartTagComment *C) {
|
|
113 CurrentCI.Name = C->getTagName();
|
|
114 CurrentCI.SelfClosing = C->isSelfClosing();
|
|
115 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
|
|
116 const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
|
|
117 CurrentCI.AttrKeys.push_back(Attr.Name);
|
|
118 CurrentCI.AttrValues.push_back(Attr.Value);
|
|
119 }
|
|
120 }
|
|
121
|
|
122 void ClangDocCommentVisitor::visitHTMLEndTagComment(
|
|
123 const HTMLEndTagComment *C) {
|
|
124 CurrentCI.Name = C->getTagName();
|
|
125 CurrentCI.SelfClosing = true;
|
|
126 }
|
|
127
|
|
128 void ClangDocCommentVisitor::visitBlockCommandComment(
|
|
129 const BlockCommandComment *C) {
|
|
130 CurrentCI.Name = getCommandName(C->getCommandID());
|
|
131 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
|
|
132 CurrentCI.Args.push_back(C->getArgText(I));
|
|
133 }
|
|
134
|
|
135 void ClangDocCommentVisitor::visitParamCommandComment(
|
|
136 const ParamCommandComment *C) {
|
|
137 CurrentCI.Direction =
|
|
138 ParamCommandComment::getDirectionAsString(C->getDirection());
|
|
139 CurrentCI.Explicit = C->isDirectionExplicit();
|
|
140 if (C->hasParamName())
|
|
141 CurrentCI.ParamName = C->getParamNameAsWritten();
|
|
142 }
|
|
143
|
|
144 void ClangDocCommentVisitor::visitTParamCommandComment(
|
|
145 const TParamCommandComment *C) {
|
|
146 if (C->hasParamName())
|
|
147 CurrentCI.ParamName = C->getParamNameAsWritten();
|
|
148 }
|
|
149
|
|
150 void ClangDocCommentVisitor::visitVerbatimBlockComment(
|
|
151 const VerbatimBlockComment *C) {
|
|
152 CurrentCI.Name = getCommandName(C->getCommandID());
|
|
153 CurrentCI.CloseName = C->getCloseName();
|
|
154 }
|
|
155
|
|
156 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
|
|
157 const VerbatimBlockLineComment *C) {
|
|
158 if (!isWhitespaceOnly(C->getText()))
|
|
159 CurrentCI.Text = C->getText();
|
|
160 }
|
|
161
|
|
162 void ClangDocCommentVisitor::visitVerbatimLineComment(
|
|
163 const VerbatimLineComment *C) {
|
|
164 if (!isWhitespaceOnly(C->getText()))
|
|
165 CurrentCI.Text = C->getText();
|
|
166 }
|
|
167
|
|
168 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
|
|
169 return std::all_of(S.begin(), S.end(), isspace);
|
|
170 }
|
|
171
|
|
172 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
|
|
173 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
|
|
174 if (Info)
|
|
175 return Info->Name;
|
|
176 // TODO: Add parsing for \file command.
|
|
177 return "<not a builtin command>";
|
|
178 }
|
|
179
|
|
180 // Serializing functions.
|
|
181
|
|
182 template <typename T> static std::string serialize(T &I) {
|
|
183 SmallString<2048> Buffer;
|
|
184 llvm::BitstreamWriter Stream(Buffer);
|
|
185 ClangDocBitcodeWriter Writer(Stream);
|
|
186 Writer.emitBlock(I);
|
|
187 return Buffer.str().str();
|
|
188 }
|
|
189
|
|
190 std::string serialize(std::unique_ptr<Info> &I) {
|
|
191 switch (I->IT) {
|
|
192 case InfoType::IT_namespace:
|
|
193 return serialize(*static_cast<NamespaceInfo *>(I.get()));
|
|
194 case InfoType::IT_record:
|
|
195 return serialize(*static_cast<RecordInfo *>(I.get()));
|
|
196 case InfoType::IT_enum:
|
|
197 return serialize(*static_cast<EnumInfo *>(I.get()));
|
|
198 case InfoType::IT_function:
|
|
199 return serialize(*static_cast<FunctionInfo *>(I.get()));
|
|
200 default:
|
|
201 return "";
|
|
202 }
|
|
203 }
|
|
204
|
|
205 static void parseFullComment(const FullComment *C, CommentInfo &CI) {
|
|
206 ClangDocCommentVisitor Visitor(CI);
|
|
207 Visitor.parseComment(C);
|
|
208 }
|
|
209
|
|
210 static SymbolID getUSRForDecl(const Decl *D) {
|
|
211 llvm::SmallString<128> USR;
|
|
212 if (index::generateUSRForDecl(D, USR))
|
|
213 return SymbolID();
|
|
214 return hashUSR(USR);
|
|
215 }
|
|
216
|
|
217 static RecordDecl *getDeclForType(const QualType &T) {
|
|
218 if (const RecordDecl *D = T->getAsRecordDecl())
|
|
219 return D->getDefinition();
|
|
220 return nullptr;
|
|
221 }
|
|
222
|
|
223 static bool isPublic(const clang::AccessSpecifier AS,
|
|
224 const clang::Linkage Link) {
|
|
225 if (AS == clang::AccessSpecifier::AS_private)
|
|
226 return false;
|
|
227 else if ((Link == clang::Linkage::ModuleLinkage) ||
|
|
228 (Link == clang::Linkage::ExternalLinkage))
|
|
229 return true;
|
|
230 return false; // otherwise, linkage is some form of internal linkage
|
|
231 }
|
|
232
|
|
233 static 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.
|
|
258 static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
|
|
259 AccessSpecifier SecondAS) {
|
|
260 if (FirstAS == AccessSpecifier::AS_none ||
|
|
261 SecondAS == AccessSpecifier::AS_none)
|
|
262 return AccessSpecifier::AS_none;
|
|
263 if (FirstAS == AccessSpecifier::AS_private ||
|
|
264 SecondAS == AccessSpecifier::AS_private)
|
|
265 return AccessSpecifier::AS_private;
|
|
266 if (FirstAS == AccessSpecifier::AS_protected ||
|
|
267 SecondAS == AccessSpecifier::AS_protected)
|
|
268 return AccessSpecifier::AS_protected;
|
|
269 return AccessSpecifier::AS_public;
|
|
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
|
|
274 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
|
|
275 AccessSpecifier Access = AccessSpecifier::AS_public) {
|
|
276 for (const FieldDecl *F : D->fields()) {
|
|
277 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
|
|
278 continue;
|
|
279 if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
|
|
280 // Use getAccessUnsafe so that we just get the default AS_none if it's not
|
|
281 // valid, as opposed to an assert.
|
|
282 if (const auto *N = dyn_cast<EnumDecl>(T)) {
|
|
283 I.Members.emplace_back(
|
|
284 getUSRForDecl(T), N->getNameAsString(), InfoType::IT_enum,
|
|
285 getInfoRelativePath(N), F->getNameAsString(),
|
|
286 getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
|
|
287 continue;
|
|
288 } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
|
|
289 I.Members.emplace_back(
|
|
290 getUSRForDecl(T), N->getNameAsString(), InfoType::IT_record,
|
|
291 getInfoRelativePath(N), F->getNameAsString(),
|
|
292 getFinalAccessSpecifier(Access, N->getAccessUnsafe()));
|
|
293 continue;
|
|
294 }
|
|
295 }
|
|
296 I.Members.emplace_back(
|
|
297 F->getTypeSourceInfo()->getType().getAsString(), F->getNameAsString(),
|
|
298 getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
|
|
299 }
|
|
300 }
|
|
301
|
|
302 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
|
|
303 for (const EnumConstantDecl *E : D->enumerators())
|
|
304 I.Members.emplace_back(E->getNameAsString());
|
|
305 }
|
|
306
|
|
307 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
|
|
308 for (const ParmVarDecl *P : D->parameters()) {
|
|
309 if (const auto *T = getDeclForType(P->getOriginalType())) {
|
|
310 if (const auto *N = dyn_cast<EnumDecl>(T)) {
|
|
311 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
|
312 InfoType::IT_enum, getInfoRelativePath(N),
|
|
313 P->getNameAsString());
|
|
314 continue;
|
|
315 } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
|
|
316 I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
|
317 InfoType::IT_record, getInfoRelativePath(N),
|
|
318 P->getNameAsString());
|
|
319 continue;
|
|
320 }
|
|
321 }
|
|
322 I.Params.emplace_back(P->getOriginalType().getAsString(),
|
|
323 P->getNameAsString());
|
|
324 }
|
|
325 }
|
|
326
|
|
327 // TODO: Remove the serialization of Parents and VirtualParents, this
|
|
328 // information is also extracted in the other definition of parseBases.
|
|
329 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
|
|
330 // Don't parse bases if this isn't a definition.
|
|
331 if (!D->isThisDeclarationADefinition())
|
|
332 return;
|
|
333 for (const CXXBaseSpecifier &B : D->bases()) {
|
|
334 if (B.isVirtual())
|
|
335 continue;
|
|
336 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
|
|
337 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
|
|
338 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
|
|
339 InfoType::IT_record);
|
|
340 } else if (const RecordDecl *P = getDeclForType(B.getType()))
|
|
341 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
|
|
342 InfoType::IT_record, getInfoRelativePath(P));
|
|
343 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()))
|
|
348 I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
|
|
349 InfoType::IT_record,
|
|
350 getInfoRelativePath(P));
|
|
351 else
|
|
352 I.VirtualParents.emplace_back(B.getType().getAsString());
|
|
353 }
|
|
354 }
|
|
355
|
|
356 template <typename T>
|
|
357 static void
|
|
358 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
|
|
359 const T *D, bool &IsInAnonymousNamespace) {
|
|
360 const auto *DC = dyn_cast<DeclContext>(D);
|
|
361 while ((DC = DC->getParent())) {
|
|
362 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
|
|
363 std::string Namespace;
|
|
364 if (N->isAnonymousNamespace()) {
|
|
365 Namespace = "@nonymous_namespace";
|
|
366 IsInAnonymousNamespace = true;
|
|
367 } else
|
|
368 Namespace = N->getNameAsString();
|
|
369 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
|
|
370 InfoType::IT_namespace);
|
|
371 } else if (const auto *N = dyn_cast<RecordDecl>(DC))
|
|
372 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
|
373 InfoType::IT_record);
|
|
374 else if (const auto *N = dyn_cast<FunctionDecl>(DC))
|
|
375 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
|
376 InfoType::IT_function);
|
|
377 else if (const auto *N = dyn_cast<EnumDecl>(DC))
|
|
378 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
|
379 InfoType::IT_enum);
|
|
380 }
|
|
381 // The global namespace should be added to the list of namespaces if the decl
|
|
382 // corresponds to a Record and if it doesn't have any namespace (because this
|
|
383 // means it's in the global namespace). Also if its outermost namespace is a
|
|
384 // record because that record matches the previous condition mentioned.
|
|
385 if ((Namespaces.empty() && dyn_cast<RecordDecl>(D)) ||
|
|
386 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
|
|
387 Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
|
|
388 InfoType::IT_namespace);
|
|
389 }
|
|
390
|
|
391 template <typename T>
|
|
392 static void populateInfo(Info &I, const T *D, const FullComment *C,
|
|
393 bool &IsInAnonymousNamespace) {
|
|
394 I.USR = getUSRForDecl(D);
|
|
395 I.Name = D->getNameAsString();
|
|
396 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
|
|
397 if (C) {
|
|
398 I.Description.emplace_back();
|
|
399 parseFullComment(C, I.Description.back());
|
|
400 }
|
|
401 }
|
|
402
|
|
403 template <typename T>
|
|
404 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
|
|
405 int LineNumber, StringRef Filename,
|
|
406 bool IsFileInRootDir,
|
|
407 bool &IsInAnonymousNamespace) {
|
|
408 populateInfo(I, D, C, IsInAnonymousNamespace);
|
|
409 if (D->isThisDeclarationADefinition())
|
|
410 I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
|
|
411 else
|
|
412 I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
|
|
413 }
|
|
414
|
|
415 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
|
|
416 const FullComment *FC, int LineNumber,
|
|
417 StringRef Filename, bool IsFileInRootDir,
|
|
418 bool &IsInAnonymousNamespace) {
|
|
419 populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
|
|
420 IsInAnonymousNamespace);
|
|
421 if (const auto *T = getDeclForType(D->getReturnType())) {
|
|
422 if (dyn_cast<EnumDecl>(T))
|
|
423 I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
|
|
424 InfoType::IT_enum, getInfoRelativePath(T));
|
|
425 else if (dyn_cast<RecordDecl>(T))
|
|
426 I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
|
|
427 InfoType::IT_record, getInfoRelativePath(T));
|
|
428 } else {
|
|
429 I.ReturnType = TypeInfo(D->getReturnType().getAsString());
|
|
430 }
|
|
431 parseParameters(I, D);
|
|
432 }
|
|
433
|
|
434 static void
|
|
435 parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
|
|
436 bool PublicOnly, bool IsParent,
|
|
437 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
|
|
438 // Don't parse bases if this isn't a definition.
|
|
439 if (!D->isThisDeclarationADefinition())
|
|
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
|
|
490 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
491 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
|
|
492 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
493 auto I = std::make_unique<NamespaceInfo>();
|
|
494 bool IsInAnonymousNamespace = false;
|
|
495 populateInfo(*I, D, FC, IsInAnonymousNamespace);
|
|
496 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
497 return {};
|
|
498
|
|
499 I->Name = D->isAnonymousNamespace()
|
|
500 ? llvm::SmallString<16>("@nonymous_namespace")
|
|
501 : I->Name;
|
|
502 I->Path = getInfoRelativePath(I->Namespace);
|
|
503 if (I->Namespace.empty() && I->USR == SymbolID())
|
|
504 return {std::unique_ptr<Info>{std::move(I)}, nullptr};
|
|
505
|
|
506 auto ParentI = std::make_unique<NamespaceInfo>();
|
|
507 ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
|
|
508 ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace,
|
|
509 getInfoRelativePath(I->Namespace));
|
|
510 if (I->Namespace.empty())
|
|
511 ParentI->Path = getInfoRelativePath(ParentI->Namespace);
|
|
512 return {std::unique_ptr<Info>{std::move(I)},
|
|
513 std::unique_ptr<Info>{std::move(ParentI)}};
|
|
514 }
|
|
515
|
|
516 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
517 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
|
|
518 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
519 auto I = std::make_unique<RecordInfo>();
|
|
520 bool IsInAnonymousNamespace = false;
|
|
521 populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
|
|
522 IsInAnonymousNamespace);
|
|
523 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
524 return {};
|
|
525
|
|
526 I->TagType = D->getTagKind();
|
|
527 parseFields(*I, D, PublicOnly);
|
|
528 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
|
|
529 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
|
|
530 I->Name = TD->getNameAsString();
|
|
531 I->IsTypeDef = true;
|
|
532 }
|
|
533 // TODO: remove first call to parseBases, that function should be deleted
|
|
534 parseBases(*I, C);
|
|
535 parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
|
|
536 }
|
|
537 I->Path = getInfoRelativePath(I->Namespace);
|
|
538
|
|
539 switch (I->Namespace[0].RefType) {
|
|
540 case InfoType::IT_namespace: {
|
|
541 auto ParentI = std::make_unique<NamespaceInfo>();
|
|
542 ParentI->USR = I->Namespace[0].USR;
|
|
543 ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
|
|
544 getInfoRelativePath(I->Namespace));
|
|
545 return {std::unique_ptr<Info>{std::move(I)},
|
|
546 std::unique_ptr<Info>{std::move(ParentI)}};
|
|
547 }
|
|
548 case InfoType::IT_record: {
|
|
549 auto ParentI = std::make_unique<RecordInfo>();
|
|
550 ParentI->USR = I->Namespace[0].USR;
|
|
551 ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
|
|
552 getInfoRelativePath(I->Namespace));
|
|
553 return {std::unique_ptr<Info>{std::move(I)},
|
|
554 std::unique_ptr<Info>{std::move(ParentI)}};
|
|
555 }
|
|
556 default:
|
|
557 llvm_unreachable("Invalid reference type for parent namespace");
|
|
558 }
|
|
559 }
|
|
560
|
|
561 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
562 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
|
|
563 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
564 FunctionInfo Func;
|
|
565 bool IsInAnonymousNamespace = false;
|
|
566 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
|
567 IsInAnonymousNamespace);
|
|
568 Func.Access = clang::AccessSpecifier::AS_none;
|
|
569 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
570 return {};
|
|
571
|
|
572 // Wrap in enclosing scope
|
|
573 auto ParentI = std::make_unique<NamespaceInfo>();
|
|
574 if (!Func.Namespace.empty())
|
|
575 ParentI->USR = Func.Namespace[0].USR;
|
|
576 else
|
|
577 ParentI->USR = SymbolID();
|
|
578 if (Func.Namespace.empty())
|
|
579 ParentI->Path = getInfoRelativePath(ParentI->Namespace);
|
|
580 ParentI->ChildFunctions.emplace_back(std::move(Func));
|
|
581 // Info is wrapped in its parent scope so it's returned in the second position
|
|
582 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
|
|
583 }
|
|
584
|
|
585 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
586 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
|
|
587 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
588 FunctionInfo Func;
|
|
589 bool IsInAnonymousNamespace = false;
|
|
590 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
|
591 IsInAnonymousNamespace);
|
|
592 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
593 return {};
|
|
594
|
|
595 Func.IsMethod = true;
|
|
596
|
|
597 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};
|
|
607 Func.Access = D->getAccess();
|
|
608
|
|
609 // Wrap in enclosing scope
|
|
610 auto ParentI = std::make_unique<RecordInfo>();
|
|
611 ParentI->USR = ParentUSR;
|
|
612 ParentI->ChildFunctions.emplace_back(std::move(Func));
|
|
613 // Info is wrapped in its parent scope so it's returned in the second position
|
|
614 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
|
|
615 }
|
|
616
|
|
617 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
618 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
|
|
619 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
620 EnumInfo Enum;
|
|
621 bool IsInAnonymousNamespace = false;
|
|
622 populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
|
|
623 IsInAnonymousNamespace);
|
|
624 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
625 return {};
|
|
626
|
|
627 Enum.Scoped = D->isScoped();
|
|
628 parseEnumerators(Enum, D);
|
|
629
|
|
630 // Put in global namespace
|
|
631 if (Enum.Namespace.empty()) {
|
|
632 auto ParentI = std::make_unique<NamespaceInfo>();
|
|
633 ParentI->USR = SymbolID();
|
|
634 ParentI->ChildEnums.emplace_back(std::move(Enum));
|
|
635 ParentI->Path = getInfoRelativePath(ParentI->Namespace);
|
|
636 // Info is wrapped in its parent scope so it's returned in the second
|
|
637 // position
|
|
638 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
|
|
639 }
|
|
640
|
|
641 // Wrap in enclosing scope
|
|
642 switch (Enum.Namespace[0].RefType) {
|
|
643 case InfoType::IT_namespace: {
|
|
644 auto ParentI = std::make_unique<NamespaceInfo>();
|
|
645 ParentI->USR = Enum.Namespace[0].USR;
|
|
646 ParentI->ChildEnums.emplace_back(std::move(Enum));
|
|
647 // Info is wrapped in its parent scope so it's returned in the second
|
|
648 // position
|
|
649 return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
|
|
650 }
|
|
651 case InfoType::IT_record: {
|
|
652 auto ParentI = std::make_unique<RecordInfo>();
|
|
653 ParentI->USR = Enum.Namespace[0].USR;
|
|
654 ParentI->ChildEnums.emplace_back(std::move(Enum));
|
|
655 // Info is wrapped in its parent scope so it's returned in the second
|
|
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 }
|
|
662 }
|
|
663
|
|
664 } // namespace serialize
|
|
665 } // namespace doc
|
|
666 } // namespace clang
|