File: | clang-tools-extra/clang-doc/Serialize.cpp |
Warning: | line 361, column 16 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 { | |||
39 | // namespace B { | |||
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() && isa<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 (isa<EnumDecl>(T)) | |||
423 | I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), | |||
424 | InfoType::IT_enum, getInfoRelativePath(T)); | |||
425 | else if (isa<RecordDecl>(T)) | |||
426 | I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(), | |||
427 | InfoType::IT_record, getInfoRelativePath(T)); | |||
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")::llvm::llvm_unreachable_internal("Invalid reference type for parent namespace" , "clang-tools-extra/clang-doc/Serialize.cpp", 557); | |||
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")::llvm::llvm_unreachable_internal("Invalid reference type for parent namespace" , "clang-tools-extra/clang-doc/Serialize.cpp", 660); | |||
661 | } | |||
662 | } | |||
663 | ||||
664 | } // namespace serialize | |||
665 | } // namespace doc | |||
666 | } // namespace clang |