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"
|
236
|
13 #include "clang/Lex/Lexer.h"
|
150
|
14 #include "llvm/ADT/Hashing.h"
|
|
15 #include "llvm/ADT/StringExtras.h"
|
|
16 #include "llvm/Support/SHA1.h"
|
|
17
|
|
18 using clang::comments::FullComment;
|
|
19
|
|
20 namespace clang {
|
|
21 namespace doc {
|
|
22 namespace serialize {
|
|
23
|
|
24 SymbolID hashUSR(llvm::StringRef USR) {
|
|
25 return llvm::SHA1::hash(arrayRefFromStringRef(USR));
|
|
26 }
|
|
27
|
|
28 template <typename T>
|
|
29 static void
|
|
30 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
|
|
31 const T *D, bool &IsAnonymousNamespace);
|
|
32
|
236
|
33 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
|
|
34
|
150
|
35 // A function to extract the appropriate relative path for a given info's
|
|
36 // documentation. The path returned is a composite of the parent namespaces.
|
|
37 //
|
|
38 // Example: Given the below, the directory path for class C info will be
|
|
39 // <root>/A/B
|
|
40 //
|
|
41 // namespace A {
|
173
|
42 // namespace B {
|
150
|
43 //
|
|
44 // class C {};
|
|
45 //
|
|
46 // }
|
|
47 // }
|
|
48 llvm::SmallString<128>
|
|
49 getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
|
|
50 llvm::SmallString<128> Path;
|
|
51 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
|
|
52 llvm::sys::path::append(Path, R->Name);
|
|
53 return Path;
|
|
54 }
|
|
55
|
|
56 llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
|
|
57 llvm::SmallVector<Reference, 4> Namespaces;
|
|
58 // The third arg in populateParentNamespaces is a boolean passed by reference,
|
|
59 // its value is not relevant in here so it's not used anywhere besides the
|
|
60 // function call
|
|
61 bool B = true;
|
|
62 populateParentNamespaces(Namespaces, D, B);
|
|
63 return getInfoRelativePath(Namespaces);
|
|
64 }
|
|
65
|
|
66 class ClangDocCommentVisitor
|
|
67 : public ConstCommentVisitor<ClangDocCommentVisitor> {
|
|
68 public:
|
|
69 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
|
|
70
|
|
71 void parseComment(const comments::Comment *C);
|
|
72
|
|
73 void visitTextComment(const TextComment *C);
|
|
74 void visitInlineCommandComment(const InlineCommandComment *C);
|
|
75 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
|
|
76 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
|
|
77 void visitBlockCommandComment(const BlockCommandComment *C);
|
|
78 void visitParamCommandComment(const ParamCommandComment *C);
|
|
79 void visitTParamCommandComment(const TParamCommandComment *C);
|
|
80 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
|
|
81 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
|
|
82 void visitVerbatimLineComment(const VerbatimLineComment *C);
|
|
83
|
|
84 private:
|
|
85 std::string getCommandName(unsigned CommandID) const;
|
|
86 bool isWhitespaceOnly(StringRef S) const;
|
|
87
|
|
88 CommentInfo &CurrentCI;
|
|
89 };
|
|
90
|
|
91 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
|
|
92 CurrentCI.Kind = C->getCommentKindName();
|
|
93 ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
|
|
94 for (comments::Comment *Child :
|
|
95 llvm::make_range(C->child_begin(), C->child_end())) {
|
|
96 CurrentCI.Children.emplace_back(std::make_unique<CommentInfo>());
|
|
97 ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
|
|
98 Visitor.parseComment(Child);
|
|
99 }
|
|
100 }
|
|
101
|
|
102 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
|
|
103 if (!isWhitespaceOnly(C->getText()))
|
|
104 CurrentCI.Text = C->getText();
|
|
105 }
|
|
106
|
|
107 void ClangDocCommentVisitor::visitInlineCommandComment(
|
|
108 const InlineCommandComment *C) {
|
|
109 CurrentCI.Name = getCommandName(C->getCommandID());
|
|
110 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
|
|
111 CurrentCI.Args.push_back(C->getArgText(I));
|
|
112 }
|
|
113
|
|
114 void ClangDocCommentVisitor::visitHTMLStartTagComment(
|
|
115 const HTMLStartTagComment *C) {
|
|
116 CurrentCI.Name = C->getTagName();
|
|
117 CurrentCI.SelfClosing = C->isSelfClosing();
|
|
118 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
|
|
119 const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
|
|
120 CurrentCI.AttrKeys.push_back(Attr.Name);
|
|
121 CurrentCI.AttrValues.push_back(Attr.Value);
|
|
122 }
|
|
123 }
|
|
124
|
|
125 void ClangDocCommentVisitor::visitHTMLEndTagComment(
|
|
126 const HTMLEndTagComment *C) {
|
|
127 CurrentCI.Name = C->getTagName();
|
|
128 CurrentCI.SelfClosing = true;
|
|
129 }
|
|
130
|
|
131 void ClangDocCommentVisitor::visitBlockCommandComment(
|
|
132 const BlockCommandComment *C) {
|
|
133 CurrentCI.Name = getCommandName(C->getCommandID());
|
|
134 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
|
|
135 CurrentCI.Args.push_back(C->getArgText(I));
|
|
136 }
|
|
137
|
|
138 void ClangDocCommentVisitor::visitParamCommandComment(
|
|
139 const ParamCommandComment *C) {
|
|
140 CurrentCI.Direction =
|
|
141 ParamCommandComment::getDirectionAsString(C->getDirection());
|
|
142 CurrentCI.Explicit = C->isDirectionExplicit();
|
|
143 if (C->hasParamName())
|
|
144 CurrentCI.ParamName = C->getParamNameAsWritten();
|
|
145 }
|
|
146
|
|
147 void ClangDocCommentVisitor::visitTParamCommandComment(
|
|
148 const TParamCommandComment *C) {
|
|
149 if (C->hasParamName())
|
|
150 CurrentCI.ParamName = C->getParamNameAsWritten();
|
|
151 }
|
|
152
|
|
153 void ClangDocCommentVisitor::visitVerbatimBlockComment(
|
|
154 const VerbatimBlockComment *C) {
|
|
155 CurrentCI.Name = getCommandName(C->getCommandID());
|
|
156 CurrentCI.CloseName = C->getCloseName();
|
|
157 }
|
|
158
|
|
159 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
|
|
160 const VerbatimBlockLineComment *C) {
|
|
161 if (!isWhitespaceOnly(C->getText()))
|
|
162 CurrentCI.Text = C->getText();
|
|
163 }
|
|
164
|
|
165 void ClangDocCommentVisitor::visitVerbatimLineComment(
|
|
166 const VerbatimLineComment *C) {
|
|
167 if (!isWhitespaceOnly(C->getText()))
|
|
168 CurrentCI.Text = C->getText();
|
|
169 }
|
|
170
|
|
171 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
|
236
|
172 return llvm::all_of(S, isspace);
|
150
|
173 }
|
|
174
|
|
175 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
|
|
176 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
|
|
177 if (Info)
|
|
178 return Info->Name;
|
|
179 // TODO: Add parsing for \file command.
|
|
180 return "<not a builtin command>";
|
|
181 }
|
|
182
|
|
183 // Serializing functions.
|
|
184
|
236
|
185 std::string getSourceCode(const Decl *D, const SourceRange &R) {
|
|
186 return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
|
|
187 D->getASTContext().getSourceManager(),
|
|
188 D->getASTContext().getLangOpts())
|
|
189 .str();
|
|
190 }
|
|
191
|
150
|
192 template <typename T> static std::string serialize(T &I) {
|
|
193 SmallString<2048> Buffer;
|
|
194 llvm::BitstreamWriter Stream(Buffer);
|
|
195 ClangDocBitcodeWriter Writer(Stream);
|
|
196 Writer.emitBlock(I);
|
|
197 return Buffer.str().str();
|
|
198 }
|
|
199
|
|
200 std::string serialize(std::unique_ptr<Info> &I) {
|
|
201 switch (I->IT) {
|
|
202 case InfoType::IT_namespace:
|
|
203 return serialize(*static_cast<NamespaceInfo *>(I.get()));
|
|
204 case InfoType::IT_record:
|
|
205 return serialize(*static_cast<RecordInfo *>(I.get()));
|
|
206 case InfoType::IT_enum:
|
|
207 return serialize(*static_cast<EnumInfo *>(I.get()));
|
|
208 case InfoType::IT_function:
|
|
209 return serialize(*static_cast<FunctionInfo *>(I.get()));
|
|
210 default:
|
|
211 return "";
|
|
212 }
|
|
213 }
|
|
214
|
|
215 static void parseFullComment(const FullComment *C, CommentInfo &CI) {
|
|
216 ClangDocCommentVisitor Visitor(CI);
|
|
217 Visitor.parseComment(C);
|
|
218 }
|
|
219
|
|
220 static SymbolID getUSRForDecl(const Decl *D) {
|
|
221 llvm::SmallString<128> USR;
|
|
222 if (index::generateUSRForDecl(D, USR))
|
|
223 return SymbolID();
|
|
224 return hashUSR(USR);
|
|
225 }
|
|
226
|
236
|
227 static TagDecl *getTagDeclForType(const QualType &T) {
|
|
228 if (const TagDecl *D = T->getAsTagDecl())
|
|
229 return D->getDefinition();
|
|
230 return nullptr;
|
|
231 }
|
|
232
|
|
233 static RecordDecl *getRecordDeclForType(const QualType &T) {
|
150
|
234 if (const RecordDecl *D = T->getAsRecordDecl())
|
|
235 return D->getDefinition();
|
|
236 return nullptr;
|
|
237 }
|
|
238
|
236
|
239 TypeInfo getTypeInfoForType(const QualType &T) {
|
|
240 const TagDecl *TD = getTagDeclForType(T);
|
|
241 if (!TD)
|
|
242 return TypeInfo(Reference(SymbolID(), T.getAsString()));
|
|
243
|
|
244 InfoType IT;
|
|
245 if (dyn_cast<EnumDecl>(TD)) {
|
|
246 IT = InfoType::IT_enum;
|
|
247 } else if (dyn_cast<RecordDecl>(TD)) {
|
|
248 IT = InfoType::IT_record;
|
|
249 } else {
|
|
250 IT = InfoType::IT_default;
|
|
251 }
|
|
252 return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
|
252
|
253 T.getAsString(), getInfoRelativePath(TD)));
|
236
|
254 }
|
|
255
|
150
|
256 static bool isPublic(const clang::AccessSpecifier AS,
|
|
257 const clang::Linkage Link) {
|
|
258 if (AS == clang::AccessSpecifier::AS_private)
|
|
259 return false;
|
|
260 else if ((Link == clang::Linkage::ModuleLinkage) ||
|
|
261 (Link == clang::Linkage::ExternalLinkage))
|
|
262 return true;
|
|
263 return false; // otherwise, linkage is some form of internal linkage
|
|
264 }
|
|
265
|
|
266 static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace,
|
|
267 const NamedDecl *D) {
|
|
268 bool IsAnonymousNamespace = false;
|
|
269 if (const auto *N = dyn_cast<NamespaceDecl>(D))
|
|
270 IsAnonymousNamespace = N->isAnonymousNamespace();
|
|
271 return !PublicOnly ||
|
|
272 (!IsInAnonymousNamespace && !IsAnonymousNamespace &&
|
|
273 isPublic(D->getAccessUnsafe(), D->getLinkageInternal()));
|
|
274 }
|
|
275
|
236
|
276 // The InsertChild functions insert the given info into the given scope using
|
|
277 // the method appropriate for that type. Some types are moved into the
|
|
278 // appropriate vector, while other types have Reference objects generated to
|
|
279 // refer to them.
|
|
280 //
|
|
281 // See MakeAndInsertIntoParent().
|
|
282 static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
|
|
283 Scope.Namespaces.emplace_back(Info.USR, Info.Name, InfoType::IT_namespace,
|
252
|
284 Info.Name, getInfoRelativePath(Info.Namespace));
|
236
|
285 }
|
|
286
|
|
287 static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
|
|
288 Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
|
252
|
289 Info.Name, getInfoRelativePath(Info.Namespace));
|
236
|
290 }
|
|
291
|
|
292 static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
|
|
293 Scope.Enums.push_back(std::move(Info));
|
|
294 }
|
|
295
|
|
296 static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
|
|
297 Scope.Functions.push_back(std::move(Info));
|
|
298 }
|
|
299
|
|
300 static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
|
|
301 Scope.Typedefs.push_back(std::move(Info));
|
|
302 }
|
|
303
|
|
304 // Creates a parent of the correct type for the given child and inserts it into
|
|
305 // that parent.
|
|
306 //
|
|
307 // This is complicated by the fact that namespaces and records are inserted by
|
|
308 // reference (constructing a "Reference" object with that namespace/record's
|
|
309 // info), while everything else is inserted by moving it directly into the child
|
|
310 // vectors.
|
|
311 //
|
|
312 // For namespaces and records, explicitly specify a const& template parameter
|
|
313 // when invoking this function:
|
|
314 // MakeAndInsertIntoParent<const Record&>(...);
|
|
315 // Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
|
|
316 // parameter. Since each variant is used once, it's not worth having a more
|
|
317 // elaborate system to automatically deduce this information.
|
|
318 template <typename ChildType>
|
|
319 std::unique_ptr<Info> MakeAndInsertIntoParent(ChildType Child) {
|
|
320 if (Child.Namespace.empty()) {
|
|
321 // Insert into unnamed parent namespace.
|
|
322 auto ParentNS = std::make_unique<NamespaceInfo>();
|
|
323 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
|
|
324 return ParentNS;
|
|
325 }
|
|
326
|
|
327 switch (Child.Namespace[0].RefType) {
|
|
328 case InfoType::IT_namespace: {
|
|
329 auto ParentNS = std::make_unique<NamespaceInfo>();
|
|
330 ParentNS->USR = Child.Namespace[0].USR;
|
|
331 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
|
|
332 return ParentNS;
|
|
333 }
|
|
334 case InfoType::IT_record: {
|
|
335 auto ParentRec = std::make_unique<RecordInfo>();
|
|
336 ParentRec->USR = Child.Namespace[0].USR;
|
|
337 InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
|
|
338 return ParentRec;
|
|
339 }
|
|
340 default:
|
|
341 llvm_unreachable("Invalid reference type for parent namespace");
|
|
342 }
|
|
343 }
|
|
344
|
150
|
345 // There are two uses for this function.
|
|
346 // 1) Getting the resulting mode of inheritance of a record.
|
|
347 // Example: class A {}; class B : private A {}; class C : public B {};
|
|
348 // It's explicit that C is publicly inherited from C and B is privately
|
|
349 // inherited from A. It's not explicit but C is also privately inherited from
|
|
350 // A. This is the AS that this function calculates. FirstAS is the
|
|
351 // inheritance mode of `class C : B` and SecondAS is the inheritance mode of
|
|
352 // `class B : A`.
|
|
353 // 2) Getting the inheritance mode of an inherited attribute / method.
|
|
354 // Example : class A { public: int M; }; class B : private A {};
|
|
355 // Class B is inherited from class A, which has a public attribute. This
|
|
356 // attribute is now part of the derived class B but it's not public. This
|
|
357 // will be private because the inheritance is private. This is the AS that
|
|
358 // this function calculates. FirstAS is the inheritance mode and SecondAS is
|
|
359 // the AS of the attribute / method.
|
|
360 static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS,
|
|
361 AccessSpecifier SecondAS) {
|
|
362 if (FirstAS == AccessSpecifier::AS_none ||
|
|
363 SecondAS == AccessSpecifier::AS_none)
|
|
364 return AccessSpecifier::AS_none;
|
|
365 if (FirstAS == AccessSpecifier::AS_private ||
|
|
366 SecondAS == AccessSpecifier::AS_private)
|
|
367 return AccessSpecifier::AS_private;
|
|
368 if (FirstAS == AccessSpecifier::AS_protected ||
|
|
369 SecondAS == AccessSpecifier::AS_protected)
|
|
370 return AccessSpecifier::AS_protected;
|
|
371 return AccessSpecifier::AS_public;
|
|
372 }
|
|
373
|
|
374 // The Access parameter is only provided when parsing the field of an inherited
|
|
375 // record, the access specification of the field depends on the inheritance mode
|
|
376 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly,
|
|
377 AccessSpecifier Access = AccessSpecifier::AS_public) {
|
|
378 for (const FieldDecl *F : D->fields()) {
|
|
379 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
|
|
380 continue;
|
236
|
381
|
|
382 // Use getAccessUnsafe so that we just get the default AS_none if it's not
|
|
383 // valid, as opposed to an assert.
|
|
384 MemberTypeInfo &NewMember = I.Members.emplace_back(
|
|
385 getTypeInfoForType(F->getTypeSourceInfo()->getType()),
|
|
386 F->getNameAsString(),
|
150
|
387 getFinalAccessSpecifier(Access, F->getAccessUnsafe()));
|
236
|
388 populateMemberTypeInfo(NewMember, F);
|
150
|
389 }
|
|
390 }
|
|
391
|
|
392 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
|
236
|
393 for (const EnumConstantDecl *E : D->enumerators()) {
|
|
394 std::string ValueExpr;
|
|
395 if (const Expr *InitExpr = E->getInitExpr())
|
|
396 ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
|
|
397
|
|
398 SmallString<16> ValueStr;
|
|
399 E->getInitVal().toString(ValueStr);
|
|
400 I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
|
|
401 }
|
150
|
402 }
|
|
403
|
|
404 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
|
|
405 for (const ParmVarDecl *P : D->parameters()) {
|
236
|
406 FieldTypeInfo &FieldInfo = I.Params.emplace_back(
|
|
407 getTypeInfoForType(P->getOriginalType()), P->getNameAsString());
|
252
|
408 FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange());
|
150
|
409 }
|
|
410 }
|
|
411
|
|
412 // TODO: Remove the serialization of Parents and VirtualParents, this
|
|
413 // information is also extracted in the other definition of parseBases.
|
|
414 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
|
|
415 // Don't parse bases if this isn't a definition.
|
|
416 if (!D->isThisDeclarationADefinition())
|
|
417 return;
|
|
418 for (const CXXBaseSpecifier &B : D->bases()) {
|
|
419 if (B.isVirtual())
|
|
420 continue;
|
|
421 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
|
|
422 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
|
|
423 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
|
252
|
424 InfoType::IT_record, B.getType().getAsString());
|
236
|
425 } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
|
150
|
426 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
|
252
|
427 InfoType::IT_record, P->getQualifiedNameAsString(),
|
|
428 getInfoRelativePath(P));
|
150
|
429 else
|
236
|
430 I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
|
150
|
431 }
|
|
432 for (const CXXBaseSpecifier &B : D->vbases()) {
|
236
|
433 if (const RecordDecl *P = getRecordDeclForType(B.getType()))
|
252
|
434 I.VirtualParents.emplace_back(
|
|
435 getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
|
|
436 P->getQualifiedNameAsString(), getInfoRelativePath(P));
|
150
|
437 else
|
236
|
438 I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
|
150
|
439 }
|
|
440 }
|
|
441
|
|
442 template <typename T>
|
|
443 static void
|
|
444 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
|
|
445 const T *D, bool &IsInAnonymousNamespace) {
|
236
|
446 const DeclContext *DC = D->getDeclContext();
|
|
447 do {
|
150
|
448 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
|
|
449 std::string Namespace;
|
|
450 if (N->isAnonymousNamespace()) {
|
|
451 Namespace = "@nonymous_namespace";
|
|
452 IsInAnonymousNamespace = true;
|
|
453 } else
|
|
454 Namespace = N->getNameAsString();
|
|
455 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
|
252
|
456 InfoType::IT_namespace,
|
|
457 N->getQualifiedNameAsString());
|
150
|
458 } else if (const auto *N = dyn_cast<RecordDecl>(DC))
|
|
459 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
252
|
460 InfoType::IT_record,
|
|
461 N->getQualifiedNameAsString());
|
150
|
462 else if (const auto *N = dyn_cast<FunctionDecl>(DC))
|
|
463 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
252
|
464 InfoType::IT_function,
|
|
465 N->getQualifiedNameAsString());
|
150
|
466 else if (const auto *N = dyn_cast<EnumDecl>(DC))
|
|
467 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
|
252
|
468 InfoType::IT_enum, N->getQualifiedNameAsString());
|
236
|
469 } while ((DC = DC->getParent()));
|
150
|
470 // The global namespace should be added to the list of namespaces if the decl
|
|
471 // corresponds to a Record and if it doesn't have any namespace (because this
|
|
472 // means it's in the global namespace). Also if its outermost namespace is a
|
|
473 // record because that record matches the previous condition mentioned.
|
236
|
474 if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
|
150
|
475 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
|
|
476 Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
|
|
477 InfoType::IT_namespace);
|
|
478 }
|
|
479
|
252
|
480 void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
|
|
481 const clang::Decl *D) {
|
|
482 if (const TemplateParameterList *ParamList =
|
|
483 D->getDescribedTemplateParams()) {
|
|
484 if (!TemplateInfo) {
|
|
485 TemplateInfo.emplace();
|
|
486 }
|
|
487 for (const NamedDecl *ND : *ParamList) {
|
|
488 TemplateInfo->Params.emplace_back(
|
|
489 getSourceCode(ND, ND->getSourceRange()));
|
|
490 }
|
|
491 }
|
|
492 }
|
|
493
|
|
494 TemplateParamInfo TemplateArgumentToInfo(const clang::Decl *D,
|
|
495 const TemplateArgument &Arg) {
|
|
496 // The TemplateArgument's pretty printing handles all the normal cases
|
|
497 // well enough for our requirements.
|
|
498 std::string Str;
|
|
499 llvm::raw_string_ostream Stream(Str);
|
|
500 Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
|
|
501 return TemplateParamInfo(Str);
|
|
502 }
|
|
503
|
150
|
504 template <typename T>
|
|
505 static void populateInfo(Info &I, const T *D, const FullComment *C,
|
|
506 bool &IsInAnonymousNamespace) {
|
|
507 I.USR = getUSRForDecl(D);
|
|
508 I.Name = D->getNameAsString();
|
|
509 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
|
|
510 if (C) {
|
|
511 I.Description.emplace_back();
|
|
512 parseFullComment(C, I.Description.back());
|
|
513 }
|
|
514 }
|
|
515
|
|
516 template <typename T>
|
|
517 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
|
|
518 int LineNumber, StringRef Filename,
|
|
519 bool IsFileInRootDir,
|
|
520 bool &IsInAnonymousNamespace) {
|
|
521 populateInfo(I, D, C, IsInAnonymousNamespace);
|
|
522 if (D->isThisDeclarationADefinition())
|
|
523 I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
|
|
524 else
|
|
525 I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
|
|
526 }
|
|
527
|
|
528 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
|
|
529 const FullComment *FC, int LineNumber,
|
|
530 StringRef Filename, bool IsFileInRootDir,
|
|
531 bool &IsInAnonymousNamespace) {
|
|
532 populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
|
|
533 IsInAnonymousNamespace);
|
236
|
534 I.ReturnType = getTypeInfoForType(D->getReturnType());
|
|
535 parseParameters(I, D);
|
252
|
536
|
|
537 PopulateTemplateParameters(I.Template, D);
|
|
538
|
|
539 // Handle function template specializations.
|
|
540 if (const FunctionTemplateSpecializationInfo *FTSI =
|
|
541 D->getTemplateSpecializationInfo()) {
|
|
542 if (!I.Template)
|
|
543 I.Template.emplace();
|
|
544 I.Template->Specialization.emplace();
|
|
545 auto &Specialization = *I.Template->Specialization;
|
|
546
|
|
547 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
|
|
548
|
|
549 // Template parameters to the specialization.
|
|
550 if (FTSI->TemplateArguments) {
|
|
551 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
|
|
552 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
|
|
553 }
|
|
554 }
|
|
555 }
|
236
|
556 }
|
|
557
|
|
558 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
|
|
559 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
|
|
560
|
|
561 ASTContext& Context = D->getASTContext();
|
|
562 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
|
|
563 // of this logic. See also similar code in Mapper.cpp.
|
|
564 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
|
|
565 if (!Comment)
|
|
566 return;
|
|
567
|
|
568 Comment->setAttached();
|
|
569 if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
|
|
570 I.Description.emplace_back();
|
|
571 parseFullComment(fc, I.Description.back());
|
150
|
572 }
|
|
573 }
|
|
574
|
|
575 static void
|
|
576 parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
|
|
577 bool PublicOnly, bool IsParent,
|
|
578 AccessSpecifier ParentAccess = AccessSpecifier::AS_public) {
|
|
579 // Don't parse bases if this isn't a definition.
|
|
580 if (!D->isThisDeclarationADefinition())
|
|
581 return;
|
|
582 for (const CXXBaseSpecifier &B : D->bases()) {
|
|
583 if (const RecordType *Ty = B.getType()->getAs<RecordType>()) {
|
|
584 if (const CXXRecordDecl *Base =
|
|
585 cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition())) {
|
|
586 // Initialized without USR and name, this will be set in the following
|
|
587 // if-else stmt.
|
|
588 BaseRecordInfo BI(
|
|
589 {}, "", getInfoRelativePath(Base), B.isVirtual(),
|
|
590 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
|
|
591 IsParent);
|
|
592 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
|
|
593 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
|
|
594 BI.USR = getUSRForDecl(D);
|
|
595 BI.Name = B.getType().getAsString();
|
|
596 } else {
|
|
597 BI.USR = getUSRForDecl(Base);
|
|
598 BI.Name = Base->getNameAsString();
|
|
599 }
|
|
600 parseFields(BI, Base, PublicOnly, BI.Access);
|
|
601 for (const auto &Decl : Base->decls())
|
|
602 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
|
|
603 // Don't serialize private methods
|
|
604 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
|
|
605 !MD->isUserProvided())
|
|
606 continue;
|
|
607 FunctionInfo FI;
|
|
608 FI.IsMethod = true;
|
|
609 // The seventh arg in populateFunctionInfo is a boolean passed by
|
|
610 // reference, its value is not relevant in here so it's not used
|
|
611 // anywhere besides the function call.
|
|
612 bool IsInAnonymousNamespace;
|
|
613 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
|
|
614 /*FileName=*/{}, IsFileInRootDir,
|
|
615 IsInAnonymousNamespace);
|
|
616 FI.Access =
|
|
617 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
|
236
|
618 BI.Children.Functions.emplace_back(std::move(FI));
|
150
|
619 }
|
|
620 I.Bases.emplace_back(std::move(BI));
|
|
621 // Call this function recursively to get the inherited classes of
|
|
622 // this base; these new bases will also get stored in the original
|
|
623 // RecordInfo: I.
|
|
624 parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
|
|
625 I.Bases.back().Access);
|
|
626 }
|
|
627 }
|
|
628 }
|
|
629 }
|
|
630
|
|
631 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
632 emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
|
|
633 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
634 auto I = std::make_unique<NamespaceInfo>();
|
|
635 bool IsInAnonymousNamespace = false;
|
|
636 populateInfo(*I, D, FC, IsInAnonymousNamespace);
|
|
637 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
638 return {};
|
|
639
|
|
640 I->Name = D->isAnonymousNamespace()
|
|
641 ? llvm::SmallString<16>("@nonymous_namespace")
|
|
642 : I->Name;
|
|
643 I->Path = getInfoRelativePath(I->Namespace);
|
|
644 if (I->Namespace.empty() && I->USR == SymbolID())
|
|
645 return {std::unique_ptr<Info>{std::move(I)}, nullptr};
|
|
646
|
236
|
647 // Namespaces are inserted into the parent by reference, so we need to return
|
|
648 // both the parent and the record itself.
|
|
649 return {std::move(I), MakeAndInsertIntoParent<const NamespaceInfo &>(*I)};
|
150
|
650 }
|
|
651
|
|
652 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
653 emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
|
|
654 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
655 auto I = std::make_unique<RecordInfo>();
|
|
656 bool IsInAnonymousNamespace = false;
|
|
657 populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
|
|
658 IsInAnonymousNamespace);
|
|
659 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
660 return {};
|
|
661
|
|
662 I->TagType = D->getTagKind();
|
|
663 parseFields(*I, D, PublicOnly);
|
|
664 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
|
|
665 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
|
|
666 I->Name = TD->getNameAsString();
|
|
667 I->IsTypeDef = true;
|
|
668 }
|
|
669 // TODO: remove first call to parseBases, that function should be deleted
|
|
670 parseBases(*I, C);
|
|
671 parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
|
|
672 }
|
|
673 I->Path = getInfoRelativePath(I->Namespace);
|
|
674
|
252
|
675 PopulateTemplateParameters(I->Template, D);
|
|
676
|
|
677 // Full and partial specializations.
|
|
678 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
|
|
679 if (!I->Template)
|
|
680 I->Template.emplace();
|
|
681 I->Template->Specialization.emplace();
|
|
682 auto &Specialization = *I->Template->Specialization;
|
|
683
|
|
684 // What this is a specialization of.
|
|
685 auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
|
|
686 if (SpecOf.is<ClassTemplateDecl *>()) {
|
|
687 Specialization.SpecializationOf =
|
|
688 getUSRForDecl(SpecOf.get<ClassTemplateDecl *>());
|
|
689 } else if (SpecOf.is<ClassTemplatePartialSpecializationDecl *>()) {
|
|
690 Specialization.SpecializationOf =
|
|
691 getUSRForDecl(SpecOf.get<ClassTemplatePartialSpecializationDecl *>());
|
|
692 }
|
|
693
|
|
694 // Parameters to the specilization. For partial specializations, get the
|
|
695 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
|
|
696 // because the non-explicit template parameters will have generated internal
|
|
697 // placeholder names rather than the names the user typed that match the
|
|
698 // template parameters.
|
|
699 if (const ClassTemplatePartialSpecializationDecl *CTPSD =
|
|
700 dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
|
|
701 if (const ASTTemplateArgumentListInfo *AsWritten =
|
|
702 CTPSD->getTemplateArgsAsWritten()) {
|
|
703 for (unsigned i = 0; i < AsWritten->getNumTemplateArgs(); i++) {
|
|
704 Specialization.Params.emplace_back(
|
|
705 getSourceCode(D, (*AsWritten)[i].getSourceRange()));
|
|
706 }
|
|
707 }
|
|
708 } else {
|
|
709 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
|
|
710 Specialization.Params.push_back(TemplateArgumentToInfo(D, Arg));
|
|
711 }
|
|
712 }
|
|
713 }
|
|
714
|
236
|
715 // Records are inserted into the parent by reference, so we need to return
|
|
716 // both the parent and the record itself.
|
|
717 auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
|
|
718 return {std::move(I), std::move(Parent)};
|
150
|
719 }
|
|
720
|
|
721 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
722 emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
|
|
723 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
724 FunctionInfo Func;
|
|
725 bool IsInAnonymousNamespace = false;
|
|
726 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
|
727 IsInAnonymousNamespace);
|
|
728 Func.Access = clang::AccessSpecifier::AS_none;
|
|
729 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
730 return {};
|
|
731
|
236
|
732 // Info is wrapped in its parent scope so is returned in the second position.
|
|
733 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
|
150
|
734 }
|
|
735
|
|
736 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
737 emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
|
|
738 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
739 FunctionInfo Func;
|
|
740 bool IsInAnonymousNamespace = false;
|
|
741 populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
|
|
742 IsInAnonymousNamespace);
|
|
743 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
744 return {};
|
|
745
|
|
746 Func.IsMethod = true;
|
|
747
|
|
748 const NamedDecl *Parent = nullptr;
|
|
749 if (const auto *SD =
|
|
750 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
|
|
751 Parent = SD->getSpecializedTemplate();
|
|
752 else
|
|
753 Parent = D->getParent();
|
|
754
|
|
755 SymbolID ParentUSR = getUSRForDecl(Parent);
|
|
756 Func.Parent =
|
252
|
757 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
|
|
758 Parent->getQualifiedNameAsString()};
|
150
|
759 Func.Access = D->getAccess();
|
|
760
|
236
|
761 // Info is wrapped in its parent scope so is returned in the second position.
|
|
762 return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
|
|
763 }
|
|
764
|
|
765 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
766 emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
|
|
767 StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
768 TypedefInfo Info;
|
|
769
|
|
770 bool IsInAnonymousNamespace = false;
|
|
771 populateInfo(Info, D, FC, IsInAnonymousNamespace);
|
|
772 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
773 return {};
|
|
774
|
|
775 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
|
|
776 Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
|
|
777 if (Info.Underlying.Type.Name.empty()) {
|
|
778 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
|
|
779 // The record serializer explicitly checks for this syntax and constructs
|
|
780 // a record with that name, so we don't want to emit a duplicate here.
|
|
781 return {};
|
|
782 }
|
|
783 Info.IsUsing = false;
|
|
784
|
|
785 // Info is wrapped in its parent scope so is returned in the second position.
|
|
786 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
|
|
787 }
|
|
788
|
|
789 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
|
|
790 // TypedefInfo with the IsUsing flag set.
|
|
791 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
792 emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
|
|
793 StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
794 TypedefInfo Info;
|
|
795
|
|
796 bool IsInAnonymousNamespace = false;
|
|
797 populateInfo(Info, D, FC, IsInAnonymousNamespace);
|
|
798 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
799 return {};
|
|
800
|
|
801 Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
|
|
802 Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
|
|
803 Info.IsUsing = true;
|
|
804
|
|
805 // Info is wrapped in its parent scope so is returned in the second position.
|
|
806 return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
|
150
|
807 }
|
|
808
|
|
809 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
|
|
810 emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
|
|
811 llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
|
|
812 EnumInfo Enum;
|
|
813 bool IsInAnonymousNamespace = false;
|
|
814 populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
|
|
815 IsInAnonymousNamespace);
|
|
816 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
|
|
817 return {};
|
|
818
|
|
819 Enum.Scoped = D->isScoped();
|
252
|
820 if (D->isFixed()) {
|
|
821 auto Name = D->getIntegerType().getAsString();
|
|
822 Enum.BaseType = TypeInfo(Name, Name);
|
|
823 }
|
150
|
824 parseEnumerators(Enum, D);
|
|
825
|
236
|
826 // Info is wrapped in its parent scope so is returned in the second position.
|
|
827 return {nullptr, MakeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
|
150
|
828 }
|
|
829
|
|
830 } // namespace serialize
|
|
831 } // namespace doc
|
|
832 } // namespace clang
|