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/Attr.h" |
12 | #include "clang/AST/Comment.h" |
13 | #include "clang/Index/USRGeneration.h" |
14 | #include "clang/Lex/Lexer.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(Data: arrayRefFromStringRef(Input: USR)); |
26 | } |
27 | |
28 | template <typename T> |
29 | static void |
30 | populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, |
31 | const T *D, bool &IsAnonymousNamespace); |
32 | |
33 | static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D); |
34 | static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, |
35 | const DeclaratorDecl *D, |
36 | bool IsStatic = false); |
37 | |
38 | static void getTemplateParameters(const TemplateParameterList *TemplateParams, |
39 | llvm::raw_ostream &Stream) { |
40 | Stream << "template <"; |
41 | |
42 | for (unsigned i = 0; i < TemplateParams->size(); ++i) { |
43 | if (i > 0) |
44 | Stream << ", "; |
45 | |
46 | const NamedDecl *Param = TemplateParams->getParam(Idx: i); |
47 | if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Val: Param)) { |
48 | if (TTP->wasDeclaredWithTypename()) |
49 | Stream << "typename"; |
50 | else |
51 | Stream << "class"; |
52 | if (TTP->isParameterPack()) |
53 | Stream << "..."; |
54 | Stream << " "<< TTP->getNameAsString(); |
55 | |
56 | // We need to also handle type constraints for code like: |
57 | // template <class T = void> |
58 | // class C {}; |
59 | if (TTP->hasTypeConstraint()) { |
60 | Stream << " = "; |
61 | TTP->getTypeConstraint()->print( |
62 | OS&: Stream, Policy: TTP->getASTContext().getPrintingPolicy()); |
63 | } |
64 | } else if (const auto *NTTP = |
65 | llvm::dyn_cast<NonTypeTemplateParmDecl>(Val: Param)) { |
66 | NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy()); |
67 | if (NTTP->isParameterPack()) |
68 | Stream << "..."; |
69 | Stream << " "<< NTTP->getNameAsString(); |
70 | } else if (const auto *TTPD = |
71 | llvm::dyn_cast<TemplateTemplateParmDecl>(Val: Param)) { |
72 | Stream << "template <"; |
73 | getTemplateParameters(TTPD->getTemplateParameters(), Stream); |
74 | Stream << "> class "<< TTPD->getNameAsString(); |
75 | } |
76 | } |
77 | |
78 | Stream << "> "; |
79 | } |
80 | |
81 | // Extract the full function prototype from a FunctionDecl including |
82 | // Full Decl |
83 | static llvm::SmallString<256> |
84 | getFunctionPrototype(const FunctionDecl *FuncDecl) { |
85 | llvm::SmallString<256> Result; |
86 | llvm::raw_svector_ostream Stream(Result); |
87 | const ASTContext &Ctx = FuncDecl->getASTContext(); |
88 | const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Val: FuncDecl); |
89 | // If it's a templated function, handle the template parameters |
90 | if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) |
91 | getTemplateParameters(TmplDecl->getTemplateParameters(), Stream); |
92 | |
93 | // If it's a virtual method |
94 | if (Method && Method->isVirtual()) |
95 | Stream << "virtual "; |
96 | |
97 | // Print return type |
98 | FuncDecl->getReturnType().print(OS&: Stream, Policy: Ctx.getPrintingPolicy()); |
99 | |
100 | // Print function name |
101 | Stream << " "<< FuncDecl->getNameAsString() << "("; |
102 | |
103 | // Print parameter list with types, names, and default values |
104 | for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) { |
105 | if (I > 0) |
106 | Stream << ", "; |
107 | const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(i: I); |
108 | QualType ParamType = ParamDecl->getType(); |
109 | ParamType.print(OS&: Stream, Policy: Ctx.getPrintingPolicy()); |
110 | |
111 | // Print parameter name if it has one |
112 | if (!ParamDecl->getName().empty()) |
113 | Stream << " "<< ParamDecl->getNameAsString(); |
114 | |
115 | // Print default argument if it exists |
116 | if (ParamDecl->hasDefaultArg() && |
117 | !ParamDecl->hasUninstantiatedDefaultArg()) { |
118 | if (const Expr *DefaultArg = ParamDecl->getDefaultArg()) { |
119 | Stream << " = "; |
120 | DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy()); |
121 | } |
122 | } |
123 | } |
124 | |
125 | // If it is a variadic function, add '...' |
126 | if (FuncDecl->isVariadic()) { |
127 | if (FuncDecl->getNumParams() > 0) |
128 | Stream << ", "; |
129 | Stream << "..."; |
130 | } |
131 | |
132 | Stream << ")"; |
133 | |
134 | // If it's a const method, add 'const' qualifier |
135 | if (Method) { |
136 | if (Method->isDeleted()) |
137 | Stream << " = delete"; |
138 | if (Method->size_overridden_methods()) |
139 | Stream << " override"; |
140 | if (Method->hasAttr<clang::FinalAttr>()) |
141 | Stream << " final"; |
142 | if (Method->isConst()) |
143 | Stream << " const"; |
144 | if (Method->isPureVirtual()) |
145 | Stream << " = 0"; |
146 | } |
147 | |
148 | if (auto ExceptionSpecType = FuncDecl->getExceptionSpecType()) |
149 | Stream << " "<< ExceptionSpecType; |
150 | |
151 | return Result; // Convert SmallString to std::string for return |
152 | } |
153 | |
154 | static llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) { |
155 | llvm::SmallString<16> Result; |
156 | llvm::raw_svector_ostream Stream(Result); |
157 | const ASTContext &Ctx = Alias->getASTContext(); |
158 | if (const auto *TmplDecl = Alias->getDescribedTemplate()) |
159 | getTemplateParameters(TmplDecl->getTemplateParameters(), Stream); |
160 | Stream << "using "<< Alias->getNameAsString() << " = "; |
161 | QualType Q = Alias->getUnderlyingType(); |
162 | Q.print(OS&: Stream, Policy: Ctx.getPrintingPolicy()); |
163 | |
164 | return Result; |
165 | } |
166 | |
167 | // extract full syntax for record declaration |
168 | static llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) { |
169 | llvm::SmallString<16> Result; |
170 | LangOptions LangOpts; |
171 | PrintingPolicy Policy(LangOpts); |
172 | Policy.SuppressTagKeyword = false; |
173 | Policy.FullyQualifiedName = true; |
174 | Policy.IncludeNewlines = false; |
175 | llvm::raw_svector_ostream OS(Result); |
176 | if (const auto *TD = CXXRD->getDescribedClassTemplate()) { |
177 | OS << "template <"; |
178 | bool FirstParam = true; |
179 | for (const auto *Param : *TD->getTemplateParameters()) { |
180 | if (!FirstParam) |
181 | OS << ", "; |
182 | Param->print(OS, Policy); |
183 | FirstParam = false; |
184 | } |
185 | OS << ">\n"; |
186 | } |
187 | |
188 | if (CXXRD->isStruct()) |
189 | OS << "struct "; |
190 | else if (CXXRD->isClass()) |
191 | OS << "class "; |
192 | else if (CXXRD->isUnion()) |
193 | OS << "union "; |
194 | |
195 | OS << CXXRD->getNameAsString(); |
196 | |
197 | // We need to make sure we have a good enough declaration to check. In the |
198 | // case where the class is a forward declaration, we'll fail assertions in |
199 | // DeclCXX. |
200 | if (CXXRD->isCompleteDefinition() && CXXRD->getNumBases() > 0) { |
201 | OS << " : "; |
202 | bool FirstBase = true; |
203 | for (const auto &Base : CXXRD->bases()) { |
204 | if (!FirstBase) |
205 | OS << ", "; |
206 | if (Base.isVirtual()) |
207 | OS << "virtual "; |
208 | OS << getAccessSpelling(AS: Base.getAccessSpecifier()) << " "; |
209 | OS << Base.getType().getAsString(Policy); |
210 | FirstBase = false; |
211 | } |
212 | } |
213 | return Result; |
214 | } |
215 | |
216 | // A function to extract the appropriate relative path for a given info's |
217 | // documentation. The path returned is a composite of the parent namespaces. |
218 | // |
219 | // Example: Given the below, the directory path for class C info will be |
220 | // <root>/A/B |
221 | // |
222 | // namespace A { |
223 | // namespace B { |
224 | // |
225 | // class C {}; |
226 | // |
227 | // } |
228 | // } |
229 | static llvm::SmallString<128> |
230 | getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference> &Namespaces) { |
231 | llvm::SmallString<128> Path; |
232 | for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R) |
233 | llvm::sys::path::append(path&: Path, a: R->Name); |
234 | return Path; |
235 | } |
236 | |
237 | static llvm::SmallString<128> getInfoRelativePath(const Decl *D) { |
238 | llvm::SmallVector<Reference, 4> Namespaces; |
239 | // The third arg in populateParentNamespaces is a boolean passed by reference, |
240 | // its value is not relevant in here so it's not used anywhere besides the |
241 | // function call |
242 | bool B = true; |
243 | populateParentNamespaces(Namespaces, D, IsInAnonymousNamespace&: B); |
244 | return getInfoRelativePath(Namespaces); |
245 | } |
246 | |
247 | class ClangDocCommentVisitor |
248 | : public ConstCommentVisitor<ClangDocCommentVisitor> { |
249 | public: |
250 | ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {} |
251 | |
252 | void parseComment(const comments::Comment *C); |
253 | |
254 | void visitTextComment(const TextComment *C); |
255 | void visitInlineCommandComment(const InlineCommandComment *C); |
256 | void visitHTMLStartTagComment(const HTMLStartTagComment *C); |
257 | void visitHTMLEndTagComment(const HTMLEndTagComment *C); |
258 | void visitBlockCommandComment(const BlockCommandComment *C); |
259 | void visitParamCommandComment(const ParamCommandComment *C); |
260 | void visitTParamCommandComment(const TParamCommandComment *C); |
261 | void visitVerbatimBlockComment(const VerbatimBlockComment *C); |
262 | void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); |
263 | void visitVerbatimLineComment(const VerbatimLineComment *C); |
264 | |
265 | private: |
266 | std::string getCommandName(unsigned CommandID) const; |
267 | bool isWhitespaceOnly(StringRef S) const; |
268 | |
269 | CommentInfo &CurrentCI; |
270 | }; |
271 | |
272 | void ClangDocCommentVisitor::parseComment(const comments::Comment *C) { |
273 | CurrentCI.Kind = stringToCommentKind(KindStr: C->getCommentKindName()); |
274 | ConstCommentVisitor<ClangDocCommentVisitor>::visit(C); |
275 | for (comments::Comment *Child : |
276 | llvm::make_range(x: C->child_begin(), y: C->child_end())) { |
277 | CurrentCI.Children.emplace_back(args: std::make_unique<CommentInfo>()); |
278 | ClangDocCommentVisitor Visitor(*CurrentCI.Children.back()); |
279 | Visitor.parseComment(C: Child); |
280 | } |
281 | } |
282 | |
283 | void ClangDocCommentVisitor::visitTextComment(const TextComment *C) { |
284 | if (!isWhitespaceOnly(S: C->getText())) |
285 | CurrentCI.Text = C->getText(); |
286 | } |
287 | |
288 | void ClangDocCommentVisitor::visitInlineCommandComment( |
289 | const InlineCommandComment *C) { |
290 | CurrentCI.Name = getCommandName(CommandID: C->getCommandID()); |
291 | for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I) |
292 | CurrentCI.Args.push_back(Elt: C->getArgText(Idx: I)); |
293 | } |
294 | |
295 | void ClangDocCommentVisitor::visitHTMLStartTagComment( |
296 | const HTMLStartTagComment *C) { |
297 | CurrentCI.Name = C->getTagName(); |
298 | CurrentCI.SelfClosing = C->isSelfClosing(); |
299 | for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) { |
300 | const HTMLStartTagComment::Attribute &Attr = C->getAttr(Idx: I); |
301 | CurrentCI.AttrKeys.push_back(Elt: Attr.Name); |
302 | CurrentCI.AttrValues.push_back(Elt: Attr.Value); |
303 | } |
304 | } |
305 | |
306 | void ClangDocCommentVisitor::visitHTMLEndTagComment( |
307 | const HTMLEndTagComment *C) { |
308 | CurrentCI.Name = C->getTagName(); |
309 | CurrentCI.SelfClosing = true; |
310 | } |
311 | |
312 | void ClangDocCommentVisitor::visitBlockCommandComment( |
313 | const BlockCommandComment *C) { |
314 | CurrentCI.Name = getCommandName(CommandID: C->getCommandID()); |
315 | for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I) |
316 | CurrentCI.Args.push_back(Elt: C->getArgText(Idx: I)); |
317 | } |
318 | |
319 | void ClangDocCommentVisitor::visitParamCommandComment( |
320 | const ParamCommandComment *C) { |
321 | CurrentCI.Direction = |
322 | ParamCommandComment::getDirectionAsString(D: C->getDirection()); |
323 | CurrentCI.Explicit = C->isDirectionExplicit(); |
324 | if (C->hasParamName()) |
325 | CurrentCI.ParamName = C->getParamNameAsWritten(); |
326 | } |
327 | |
328 | void ClangDocCommentVisitor::visitTParamCommandComment( |
329 | const TParamCommandComment *C) { |
330 | if (C->hasParamName()) |
331 | CurrentCI.ParamName = C->getParamNameAsWritten(); |
332 | } |
333 | |
334 | void ClangDocCommentVisitor::visitVerbatimBlockComment( |
335 | const VerbatimBlockComment *C) { |
336 | CurrentCI.Name = getCommandName(CommandID: C->getCommandID()); |
337 | CurrentCI.CloseName = C->getCloseName(); |
338 | } |
339 | |
340 | void ClangDocCommentVisitor::visitVerbatimBlockLineComment( |
341 | const VerbatimBlockLineComment *C) { |
342 | if (!isWhitespaceOnly(S: C->getText())) |
343 | CurrentCI.Text = C->getText(); |
344 | } |
345 | |
346 | void ClangDocCommentVisitor::visitVerbatimLineComment( |
347 | const VerbatimLineComment *C) { |
348 | if (!isWhitespaceOnly(S: C->getText())) |
349 | CurrentCI.Text = C->getText(); |
350 | } |
351 | |
352 | bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const { |
353 | return llvm::all_of(Range&: S, P: isspace); |
354 | } |
355 | |
356 | std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const { |
357 | const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID); |
358 | if (Info) |
359 | return Info->Name; |
360 | // TODO: Add parsing for \file command. |
361 | return "<not a builtin command>"; |
362 | } |
363 | |
364 | // Serializing functions. |
365 | |
366 | static std::string getSourceCode(const Decl *D, const SourceRange &R) { |
367 | return Lexer::getSourceText(Range: CharSourceRange::getTokenRange(R), |
368 | SM: D->getASTContext().getSourceManager(), |
369 | LangOpts: D->getASTContext().getLangOpts()) |
370 | .str(); |
371 | } |
372 | |
373 | template <typename T> static std::string serialize(T &I) { |
374 | SmallString<2048> Buffer; |
375 | llvm::BitstreamWriter Stream(Buffer); |
376 | ClangDocBitcodeWriter Writer(Stream); |
377 | Writer.emitBlock(I); |
378 | return Buffer.str().str(); |
379 | } |
380 | |
381 | std::string serialize(std::unique_ptr<Info> &I) { |
382 | switch (I->IT) { |
383 | case InfoType::IT_namespace: |
384 | return serialize(I&: *static_cast<NamespaceInfo *>(I.get())); |
385 | case InfoType::IT_record: |
386 | return serialize(I&: *static_cast<RecordInfo *>(I.get())); |
387 | case InfoType::IT_enum: |
388 | return serialize(I&: *static_cast<EnumInfo *>(I.get())); |
389 | case InfoType::IT_function: |
390 | return serialize(I&: *static_cast<FunctionInfo *>(I.get())); |
391 | default: |
392 | return ""; |
393 | } |
394 | } |
395 | |
396 | static void parseFullComment(const FullComment *C, CommentInfo &CI) { |
397 | ClangDocCommentVisitor Visitor(CI); |
398 | Visitor.parseComment(C); |
399 | } |
400 | |
401 | static SymbolID getUSRForDecl(const Decl *D) { |
402 | llvm::SmallString<128> USR; |
403 | if (index::generateUSRForDecl(D, Buf&: USR)) |
404 | return SymbolID(); |
405 | return hashUSR(USR); |
406 | } |
407 | |
408 | static TagDecl *getTagDeclForType(const QualType &T) { |
409 | if (const TagDecl *D = T->getAsTagDecl()) |
410 | return D->getDefinition(); |
411 | return nullptr; |
412 | } |
413 | |
414 | static RecordDecl *getRecordDeclForType(const QualType &T) { |
415 | if (const RecordDecl *D = T->getAsRecordDecl()) |
416 | return D->getDefinition(); |
417 | return nullptr; |
418 | } |
419 | |
420 | static TypeInfo getTypeInfoForType(const QualType &T, |
421 | const PrintingPolicy &Policy) { |
422 | const TagDecl *TD = getTagDeclForType(T); |
423 | if (!TD) { |
424 | TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString(Policy))); |
425 | TI.IsBuiltIn = T->isBuiltinType(); |
426 | TI.IsTemplate = T->isTemplateTypeParmType(); |
427 | return TI; |
428 | } |
429 | InfoType IT; |
430 | if (isa<EnumDecl>(Val: TD)) { |
431 | IT = InfoType::IT_enum; |
432 | } else if (isa<RecordDecl>(Val: TD)) { |
433 | IT = InfoType::IT_record; |
434 | } else { |
435 | IT = InfoType::IT_default; |
436 | } |
437 | Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT, |
438 | T.getAsString(Policy), getInfoRelativePath(TD)); |
439 | TypeInfo TI = TypeInfo(R); |
440 | TI.IsBuiltIn = T->isBuiltinType(); |
441 | TI.IsTemplate = T->isTemplateTypeParmType(); |
442 | return TI; |
443 | } |
444 | |
445 | static bool isPublic(const clang::AccessSpecifier AS, |
446 | const clang::Linkage Link) { |
447 | if (AS == clang::AccessSpecifier::AS_private) |
448 | return false; |
449 | if ((Link == clang::Linkage::Module) || (Link == clang::Linkage::External)) |
450 | return true; |
451 | return false; // otherwise, linkage is some form of internal linkage |
452 | } |
453 | |
454 | static bool shouldSerializeInfo(bool PublicOnly, bool IsInAnonymousNamespace, |
455 | const NamedDecl *D) { |
456 | bool IsAnonymousNamespace = false; |
457 | if (const auto *N = dyn_cast<NamespaceDecl>(Val: D)) |
458 | IsAnonymousNamespace = N->isAnonymousNamespace(); |
459 | return !PublicOnly || |
460 | (!IsInAnonymousNamespace && !IsAnonymousNamespace && |
461 | isPublic(D->getAccessUnsafe(), D->getLinkageInternal())); |
462 | } |
463 | |
464 | // The InsertChild functions insert the given info into the given scope using |
465 | // the method appropriate for that type. Some types are moved into the |
466 | // appropriate vector, while other types have Reference objects generated to |
467 | // refer to them. |
468 | // |
469 | // See MakeAndInsertIntoParent(). |
470 | static void InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) { |
471 | Scope.Namespaces.emplace_back(args: Info.USR, args: Info.Name, args: InfoType::IT_namespace, |
472 | args: Info.Name, args: getInfoRelativePath(Namespaces: Info.Namespace)); |
473 | } |
474 | |
475 | static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) { |
476 | Scope.Records.emplace_back(args: Info.USR, args: Info.Name, args: InfoType::IT_record, |
477 | args: Info.Name, args: getInfoRelativePath(Namespaces: Info.Namespace)); |
478 | } |
479 | |
480 | static void InsertChild(ScopeChildren &Scope, EnumInfo Info) { |
481 | Scope.Enums.push_back(x: std::move(Info)); |
482 | } |
483 | |
484 | static void InsertChild(ScopeChildren &Scope, FunctionInfo Info) { |
485 | Scope.Functions.push_back(x: std::move(Info)); |
486 | } |
487 | |
488 | static void InsertChild(ScopeChildren &Scope, TypedefInfo Info) { |
489 | Scope.Typedefs.push_back(x: std::move(Info)); |
490 | } |
491 | |
492 | // Creates a parent of the correct type for the given child and inserts it into |
493 | // that parent. |
494 | // |
495 | // This is complicated by the fact that namespaces and records are inserted by |
496 | // reference (constructing a "Reference" object with that namespace/record's |
497 | // info), while everything else is inserted by moving it directly into the child |
498 | // vectors. |
499 | // |
500 | // For namespaces and records, explicitly specify a const& template parameter |
501 | // when invoking this function: |
502 | // MakeAndInsertIntoParent<const Record&>(...); |
503 | // Otherwise, specify an rvalue reference <EnumInfo&&> and move into the |
504 | // parameter. Since each variant is used once, it's not worth having a more |
505 | // elaborate system to automatically deduce this information. |
506 | template <typename ChildType> |
507 | static std::unique_ptr<Info> makeAndInsertIntoParent(ChildType Child) { |
508 | if (Child.Namespace.empty()) { |
509 | // Insert into unnamed parent namespace. |
510 | auto ParentNS = std::make_unique<NamespaceInfo>(); |
511 | InsertChild(ParentNS->Children, std::forward<ChildType>(Child)); |
512 | return ParentNS; |
513 | } |
514 | |
515 | switch (Child.Namespace[0].RefType) { |
516 | case InfoType::IT_namespace: { |
517 | auto ParentNS = std::make_unique<NamespaceInfo>(); |
518 | ParentNS->USR = Child.Namespace[0].USR; |
519 | InsertChild(ParentNS->Children, std::forward<ChildType>(Child)); |
520 | return ParentNS; |
521 | } |
522 | case InfoType::IT_record: { |
523 | auto ParentRec = std::make_unique<RecordInfo>(); |
524 | ParentRec->USR = Child.Namespace[0].USR; |
525 | InsertChild(ParentRec->Children, std::forward<ChildType>(Child)); |
526 | return ParentRec; |
527 | } |
528 | default: |
529 | llvm_unreachable("Invalid reference type for parent namespace"); |
530 | } |
531 | } |
532 | |
533 | // There are two uses for this function. |
534 | // 1) Getting the resulting mode of inheritance of a record. |
535 | // Example: class A {}; class B : private A {}; class C : public B {}; |
536 | // It's explicit that C is publicly inherited from C and B is privately |
537 | // inherited from A. It's not explicit but C is also privately inherited from |
538 | // A. This is the AS that this function calculates. FirstAS is the |
539 | // inheritance mode of `class C : B` and SecondAS is the inheritance mode of |
540 | // `class B : A`. |
541 | // 2) Getting the inheritance mode of an inherited attribute / method. |
542 | // Example : class A { public: int M; }; class B : private A {}; |
543 | // Class B is inherited from class A, which has a public attribute. This |
544 | // attribute is now part of the derived class B but it's not public. This |
545 | // will be private because the inheritance is private. This is the AS that |
546 | // this function calculates. FirstAS is the inheritance mode and SecondAS is |
547 | // the AS of the attribute / method. |
548 | static AccessSpecifier getFinalAccessSpecifier(AccessSpecifier FirstAS, |
549 | AccessSpecifier SecondAS) { |
550 | if (FirstAS == AccessSpecifier::AS_none || |
551 | SecondAS == AccessSpecifier::AS_none) |
552 | return AccessSpecifier::AS_none; |
553 | if (FirstAS == AccessSpecifier::AS_private || |
554 | SecondAS == AccessSpecifier::AS_private) |
555 | return AccessSpecifier::AS_private; |
556 | if (FirstAS == AccessSpecifier::AS_protected || |
557 | SecondAS == AccessSpecifier::AS_protected) |
558 | return AccessSpecifier::AS_protected; |
559 | return AccessSpecifier::AS_public; |
560 | } |
561 | |
562 | // The Access parameter is only provided when parsing the field of an inherited |
563 | // record, the access specification of the field depends on the inheritance mode |
564 | static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly, |
565 | AccessSpecifier Access = AccessSpecifier::AS_public) { |
566 | for (const FieldDecl *F : D->fields()) { |
567 | if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F)) |
568 | continue; |
569 | populateMemberTypeInfo(I, Access, F); |
570 | } |
571 | const auto *CxxRD = dyn_cast<CXXRecordDecl>(Val: D); |
572 | if (!CxxRD) |
573 | return; |
574 | for (Decl *CxxDecl : CxxRD->decls()) { |
575 | auto *VD = dyn_cast<VarDecl>(CxxDecl); |
576 | if (!VD || |
577 | !shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, VD)) |
578 | continue; |
579 | |
580 | if (VD->isStaticDataMember()) |
581 | populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true); |
582 | } |
583 | } |
584 | |
585 | static void parseEnumerators(EnumInfo &I, const EnumDecl *D) { |
586 | for (const EnumConstantDecl *E : D->enumerators()) { |
587 | std::string ValueExpr; |
588 | if (const Expr *InitExpr = E->getInitExpr()) |
589 | ValueExpr = getSourceCode(D, InitExpr->getSourceRange()); |
590 | SmallString<16> ValueStr; |
591 | E->getInitVal().toString(Str&: ValueStr); |
592 | I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr); |
593 | ASTContext &Context = E->getASTContext(); |
594 | if (RawComment *Comment = |
595 | E->getASTContext().getRawCommentForDeclNoCache(E)) { |
596 | Comment->setAttached(); |
597 | if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) { |
598 | EnumValueInfo &Member = I.Members.back(); |
599 | Member.Description.emplace_back(); |
600 | parseFullComment(C: Fc, CI&: Member.Description.back()); |
601 | } |
602 | } |
603 | } |
604 | } |
605 | |
606 | static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { |
607 | auto &LO = D->getLangOpts(); |
608 | for (const ParmVarDecl *P : D->parameters()) { |
609 | FieldTypeInfo &FieldInfo = I.Params.emplace_back( |
610 | getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString()); |
611 | FieldInfo.DefaultValue = getSourceCode(D, P->getDefaultArgRange()); |
612 | } |
613 | } |
614 | |
615 | // TODO: Remove the serialization of Parents and VirtualParents, this |
616 | // information is also extracted in the other definition of parseBases. |
617 | static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { |
618 | // Don't parse bases if this isn't a definition. |
619 | if (!D->isThisDeclarationADefinition()) |
620 | return; |
621 | |
622 | for (const CXXBaseSpecifier &B : D->bases()) { |
623 | if (B.isVirtual()) |
624 | continue; |
625 | if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { |
626 | const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); |
627 | I.Parents.emplace_back(Args: getUSRForDecl(D), Args: B.getType().getAsString(), |
628 | Args: InfoType::IT_record, Args: B.getType().getAsString()); |
629 | } else if (const RecordDecl *P = getRecordDeclForType(T: B.getType())) |
630 | I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(), |
631 | InfoType::IT_record, P->getQualifiedNameAsString(), |
632 | getInfoRelativePath(P)); |
633 | else |
634 | I.Parents.emplace_back(Args: SymbolID(), Args: B.getType().getAsString()); |
635 | } |
636 | for (const CXXBaseSpecifier &B : D->vbases()) { |
637 | if (const RecordDecl *P = getRecordDeclForType(T: B.getType())) |
638 | I.VirtualParents.emplace_back( |
639 | getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record, |
640 | P->getQualifiedNameAsString(), getInfoRelativePath(P)); |
641 | else |
642 | I.VirtualParents.emplace_back(Args: SymbolID(), Args: B.getType().getAsString()); |
643 | } |
644 | } |
645 | |
646 | template <typename T> |
647 | static void |
648 | populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces, |
649 | const T *D, bool &IsInAnonymousNamespace) { |
650 | const DeclContext *DC = D->getDeclContext(); |
651 | do { |
652 | if (const auto *N = dyn_cast<NamespaceDecl>(Val: DC)) { |
653 | std::string Namespace; |
654 | if (N->isAnonymousNamespace()) { |
655 | Namespace = "@nonymous_namespace"; |
656 | IsInAnonymousNamespace = true; |
657 | } else |
658 | Namespace = N->getNameAsString(); |
659 | Namespaces.emplace_back(getUSRForDecl(N), Namespace, |
660 | InfoType::IT_namespace, |
661 | N->getQualifiedNameAsString()); |
662 | } else if (const auto *N = dyn_cast<RecordDecl>(Val: DC)) |
663 | Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), |
664 | InfoType::IT_record, |
665 | N->getQualifiedNameAsString()); |
666 | else if (const auto *N = dyn_cast<FunctionDecl>(Val: DC)) |
667 | Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), |
668 | InfoType::IT_function, |
669 | N->getQualifiedNameAsString()); |
670 | else if (const auto *N = dyn_cast<EnumDecl>(Val: DC)) |
671 | Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(), |
672 | InfoType::IT_enum, N->getQualifiedNameAsString()); |
673 | } while ((DC = DC->getParent())); |
674 | // The global namespace should be added to the list of namespaces if the decl |
675 | // corresponds to a Record and if it doesn't have any namespace (because this |
676 | // means it's in the global namespace). Also if its outermost namespace is a |
677 | // record because that record matches the previous condition mentioned. |
678 | if ((Namespaces.empty() && isa<RecordDecl>(D)) || |
679 | (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record)) |
680 | Namespaces.emplace_back(Args: SymbolID(), Args: "GlobalNamespace", |
681 | Args: InfoType::IT_namespace); |
682 | } |
683 | |
684 | static void |
685 | populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo, |
686 | const clang::Decl *D) { |
687 | if (const TemplateParameterList *ParamList = |
688 | D->getDescribedTemplateParams()) { |
689 | if (!TemplateInfo) { |
690 | TemplateInfo.emplace(); |
691 | } |
692 | for (const NamedDecl *ND : *ParamList) { |
693 | TemplateInfo->Params.emplace_back( |
694 | getSourceCode(ND, ND->getSourceRange())); |
695 | } |
696 | } |
697 | } |
698 | |
699 | static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D, |
700 | const TemplateArgument &Arg) { |
701 | // The TemplateArgument's pretty printing handles all the normal cases |
702 | // well enough for our requirements. |
703 | std::string Str; |
704 | llvm::raw_string_ostream Stream(Str); |
705 | Arg.print(Policy: PrintingPolicy(D->getLangOpts()), Out&: Stream, IncludeType: false); |
706 | return TemplateParamInfo(Str); |
707 | } |
708 | |
709 | template <typename T> |
710 | static void populateInfo(Info &I, const T *D, const FullComment *C, |
711 | bool &IsInAnonymousNamespace) { |
712 | I.USR = getUSRForDecl(D); |
713 | if (auto ConversionDecl = dyn_cast_or_null<CXXConversionDecl>(D); |
714 | ConversionDecl && ConversionDecl->getConversionType() |
715 | .getTypePtr() |
716 | ->isTemplateTypeParmType()) |
717 | I.Name = "operator "+ ConversionDecl->getConversionType().getAsString(); |
718 | else |
719 | I.Name = D->getNameAsString(); |
720 | populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace); |
721 | if (C) { |
722 | I.Description.emplace_back(); |
723 | parseFullComment(C, CI&: I.Description.back()); |
724 | } |
725 | } |
726 | |
727 | template <typename T> |
728 | static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C, |
729 | Location Loc, bool &IsInAnonymousNamespace) { |
730 | populateInfo(I, D, C, IsInAnonymousNamespace); |
731 | if (D->isThisDeclarationADefinition()) |
732 | I.DefLoc = Loc; |
733 | else |
734 | I.Loc.emplace_back(Args&: Loc); |
735 | } |
736 | |
737 | static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, |
738 | const FullComment *FC, Location Loc, |
739 | bool &IsInAnonymousNamespace) { |
740 | populateSymbolInfo(I, D, C: FC, Loc, IsInAnonymousNamespace); |
741 | auto &LO = D->getLangOpts(); |
742 | I.ReturnType = getTypeInfoForType(D->getReturnType(), LO); |
743 | I.Prototype = getFunctionPrototype(FuncDecl: D); |
744 | parseParameters(I, D); |
745 | I.IsStatic = D->isStatic(); |
746 | |
747 | populateTemplateParameters(I.Template, D); |
748 | |
749 | // Handle function template specializations. |
750 | if (const FunctionTemplateSpecializationInfo *FTSI = |
751 | D->getTemplateSpecializationInfo()) { |
752 | if (!I.Template) |
753 | I.Template.emplace(); |
754 | I.Template->Specialization.emplace(); |
755 | auto &Specialization = *I.Template->Specialization; |
756 | |
757 | Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate()); |
758 | |
759 | // Template parameters to the specialization. |
760 | if (FTSI->TemplateArguments) { |
761 | for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) { |
762 | Specialization.Params.push_back(x: convertTemplateArgToInfo(D, Arg)); |
763 | } |
764 | } |
765 | } |
766 | } |
767 | |
768 | static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) { |
769 | assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo"); |
770 | |
771 | ASTContext &Context = D->getASTContext(); |
772 | // TODO investigate whether we can use ASTContext::getCommentForDecl instead |
773 | // of this logic. See also similar code in Mapper.cpp. |
774 | RawComment *Comment = Context.getRawCommentForDeclNoCache(D); |
775 | if (!Comment) |
776 | return; |
777 | |
778 | Comment->setAttached(); |
779 | if (comments::FullComment *Fc = Comment->parse(Context, PP: nullptr, D)) { |
780 | I.Description.emplace_back(); |
781 | parseFullComment(C: Fc, CI&: I.Description.back()); |
782 | } |
783 | } |
784 | |
785 | static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access, |
786 | const DeclaratorDecl *D, bool IsStatic) { |
787 | // Use getAccessUnsafe so that we just get the default AS_none if it's not |
788 | // valid, as opposed to an assert. |
789 | MemberTypeInfo &NewMember = I.Members.emplace_back( |
790 | getTypeInfoForType(D->getTypeSourceInfo()->getType(), D->getLangOpts()), |
791 | D->getNameAsString(), |
792 | getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic); |
793 | populateMemberTypeInfo(NewMember, D); |
794 | } |
795 | |
796 | static void |
797 | parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir, |
798 | bool PublicOnly, bool IsParent, |
799 | AccessSpecifier ParentAccess = AccessSpecifier::AS_public) { |
800 | // Don't parse bases if this isn't a definition. |
801 | if (!D->isThisDeclarationADefinition()) |
802 | return; |
803 | for (const CXXBaseSpecifier &B : D->bases()) { |
804 | if (const RecordType *Ty = B.getType()->getAs<RecordType>()) { |
805 | if (const CXXRecordDecl *Base = |
806 | cast_or_null<CXXRecordDecl>(Val: Ty->getDecl()->getDefinition())) { |
807 | // Initialized without USR and name, this will be set in the following |
808 | // if-else stmt. |
809 | BaseRecordInfo BI( |
810 | {}, "", getInfoRelativePath(Base), B.isVirtual(), |
811 | getFinalAccessSpecifier(FirstAS: ParentAccess, SecondAS: B.getAccessSpecifier()), |
812 | IsParent); |
813 | if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) { |
814 | const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl(); |
815 | BI.USR = getUSRForDecl(D); |
816 | BI.Name = B.getType().getAsString(); |
817 | } else { |
818 | BI.USR = getUSRForDecl(Base); |
819 | BI.Name = Base->getNameAsString(); |
820 | } |
821 | parseFields(BI, Base, PublicOnly, BI.Access); |
822 | for (const auto &Decl : Base->decls()) |
823 | if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) { |
824 | // Don't serialize private methods |
825 | if (MD->getAccessUnsafe() == AccessSpecifier::AS_private || |
826 | !MD->isUserProvided()) |
827 | continue; |
828 | FunctionInfo FI; |
829 | FI.IsMethod = true; |
830 | FI.IsStatic = MD->isStatic(); |
831 | // The seventh arg in populateFunctionInfo is a boolean passed by |
832 | // reference, its value is not relevant in here so it's not used |
833 | // anywhere besides the function call. |
834 | bool IsInAnonymousNamespace; |
835 | populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{}, |
836 | IsInAnonymousNamespace); |
837 | FI.Access = |
838 | getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe()); |
839 | BI.Children.Functions.emplace_back(std::move(FI)); |
840 | } |
841 | I.Bases.emplace_back(args: std::move(BI)); |
842 | // Call this function recursively to get the inherited classes of |
843 | // this base; these new bases will also get stored in the original |
844 | // RecordInfo: I. |
845 | parseBases(I, D: Base, IsFileInRootDir, PublicOnly, IsParent: false, |
846 | ParentAccess: I.Bases.back().Access); |
847 | } |
848 | } |
849 | } |
850 | } |
851 | |
852 | std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> |
853 | emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc, |
854 | bool PublicOnly) { |
855 | auto NSI = std::make_unique<NamespaceInfo>(); |
856 | bool IsInAnonymousNamespace = false; |
857 | populateInfo(I&: *NSI, D, C: FC, IsInAnonymousNamespace); |
858 | if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) |
859 | return {}; |
860 | |
861 | NSI->Name = D->isAnonymousNamespace() |
862 | ? llvm::SmallString<16>("@nonymous_namespace") |
863 | : NSI->Name; |
864 | NSI->Path = getInfoRelativePath(Namespaces: NSI->Namespace); |
865 | if (NSI->Namespace.empty() && NSI->USR == SymbolID()) |
866 | return {std::unique_ptr<Info>{std::move(NSI)}, nullptr}; |
867 | |
868 | // Namespaces are inserted into the parent by reference, so we need to return |
869 | // both the parent and the record itself. |
870 | return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(Child: *NSI)}; |
871 | } |
872 | |
873 | std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> |
874 | emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc, |
875 | bool PublicOnly) { |
876 | |
877 | auto RI = std::make_unique<RecordInfo>(); |
878 | bool IsInAnonymousNamespace = false; |
879 | |
880 | populateSymbolInfo(I&: *RI, D, C: FC, Loc, IsInAnonymousNamespace); |
881 | if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) |
882 | return {}; |
883 | |
884 | RI->TagType = D->getTagKind(); |
885 | parseFields(I&: *RI, D, PublicOnly); |
886 | |
887 | if (const auto *C = dyn_cast<CXXRecordDecl>(Val: D)) { |
888 | RI->FullName = getRecordPrototype(CXXRD: C); |
889 | if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) { |
890 | RI->Name = TD->getNameAsString(); |
891 | RI->IsTypeDef = true; |
892 | } |
893 | // TODO: remove first call to parseBases, that function should be deleted |
894 | parseBases(I&: *RI, D: C); |
895 | parseBases(I&: *RI, D: C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true); |
896 | } |
897 | RI->Path = getInfoRelativePath(Namespaces: RI->Namespace); |
898 | |
899 | populateTemplateParameters(RI->Template, D); |
900 | |
901 | // Full and partial specializations. |
902 | if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(Val: D)) { |
903 | if (!RI->Template) |
904 | RI->Template.emplace(); |
905 | RI->Template->Specialization.emplace(); |
906 | auto &Specialization = *RI->Template->Specialization; |
907 | |
908 | // What this is a specialization of. |
909 | auto SpecOf = CTSD->getSpecializedTemplateOrPartial(); |
910 | if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(Val&: SpecOf)) |
911 | Specialization.SpecializationOf = getUSRForDecl(SpecTD); |
912 | else if (auto *SpecTD = |
913 | dyn_cast<ClassTemplatePartialSpecializationDecl *>(Val&: SpecOf)) |
914 | Specialization.SpecializationOf = getUSRForDecl(SpecTD); |
915 | |
916 | // Parameters to the specialization. For partial specializations, get the |
917 | // parameters "as written" from the ClassTemplatePartialSpecializationDecl |
918 | // because the non-explicit template parameters will have generated internal |
919 | // placeholder names rather than the names the user typed that match the |
920 | // template parameters. |
921 | if (const ClassTemplatePartialSpecializationDecl *CTPSD = |
922 | dyn_cast<ClassTemplatePartialSpecializationDecl>(Val: D)) { |
923 | if (const ASTTemplateArgumentListInfo *AsWritten = |
924 | CTPSD->getTemplateArgsAsWritten()) { |
925 | for (unsigned Idx = 0; Idx < AsWritten->getNumTemplateArgs(); Idx++) { |
926 | Specialization.Params.emplace_back( |
927 | args: getSourceCode(D, (*AsWritten)[Idx].getSourceRange())); |
928 | } |
929 | } |
930 | } else { |
931 | for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) { |
932 | Specialization.Params.push_back(x: convertTemplateArgToInfo(D, Arg)); |
933 | } |
934 | } |
935 | } |
936 | |
937 | // Records are inserted into the parent by reference, so we need to return |
938 | // both the parent and the record itself. |
939 | auto Parent = makeAndInsertIntoParent<const RecordInfo &>(Child: *RI); |
940 | return {std::move(RI), std::move(Parent)}; |
941 | } |
942 | |
943 | std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> |
944 | emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc, |
945 | bool PublicOnly) { |
946 | FunctionInfo Func; |
947 | bool IsInAnonymousNamespace = false; |
948 | populateFunctionInfo(I&: Func, D, FC, Loc, IsInAnonymousNamespace); |
949 | Func.Access = clang::AccessSpecifier::AS_none; |
950 | if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) |
951 | return {}; |
952 | |
953 | // Info is wrapped in its parent scope so is returned in the second position. |
954 | return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(Child: std::move(Func))}; |
955 | } |
956 | |
957 | std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> |
958 | emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc, |
959 | bool PublicOnly) { |
960 | FunctionInfo Func; |
961 | bool IsInAnonymousNamespace = false; |
962 | populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace); |
963 | if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) |
964 | return {}; |
965 | |
966 | Func.IsMethod = true; |
967 | Func.IsStatic = D->isStatic(); |
968 | |
969 | const NamedDecl *Parent = nullptr; |
970 | if (const auto *SD = |
971 | dyn_cast<ClassTemplateSpecializationDecl>(Val: D->getParent())) |
972 | Parent = SD->getSpecializedTemplate(); |
973 | else |
974 | Parent = D->getParent(); |
975 | |
976 | SymbolID ParentUSR = getUSRForDecl(Parent); |
977 | Func.Parent = |
978 | Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record, |
979 | Parent->getQualifiedNameAsString()}; |
980 | Func.Access = D->getAccess(); |
981 | |
982 | // Info is wrapped in its parent scope so is returned in the second position. |
983 | return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(Child: std::move(Func))}; |
984 | } |
985 | |
986 | static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) { |
987 | assert(D && "Invalid Decl when extracting comment"); |
988 | ASTContext &Context = D->getASTContext(); |
989 | RawComment *Comment = Context.getRawCommentForDeclNoCache(D); |
990 | if (!Comment) |
991 | return; |
992 | |
993 | Comment->setAttached(); |
994 | if (comments::FullComment *Fc = Comment->parse(Context, PP: nullptr, D)) { |
995 | Info.Description.emplace_back(); |
996 | parseFullComment(C: Fc, CI&: Info.Description.back()); |
997 | } |
998 | } |
999 | |
1000 | std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> |
1001 | emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc, |
1002 | bool PublicOnly) { |
1003 | TypedefInfo Info; |
1004 | bool IsInAnonymousNamespace = false; |
1005 | populateInfo(I&: Info, D, C: FC, IsInAnonymousNamespace); |
1006 | |
1007 | if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) |
1008 | return {}; |
1009 | |
1010 | Info.DefLoc = Loc; |
1011 | auto &LO = D->getLangOpts(); |
1012 | Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO); |
1013 | |
1014 | if (Info.Underlying.Type.Name.empty()) { |
1015 | // Typedef for an unnamed type. This is like "typedef struct { } Foo;" |
1016 | // The record serializer explicitly checks for this syntax and constructs |
1017 | // a record with that name, so we don't want to emit a duplicate here. |
1018 | return {}; |
1019 | } |
1020 | Info.IsUsing = false; |
1021 | extractCommentFromDecl(D, Info); |
1022 | |
1023 | // Info is wrapped in its parent scope so is returned in the second position. |
1024 | return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(Child: std::move(Info))}; |
1025 | } |
1026 | |
1027 | // A type alias is a C++ "using" declaration for a type. It gets mapped to a |
1028 | // TypedefInfo with the IsUsing flag set. |
1029 | std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> |
1030 | emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc, |
1031 | bool PublicOnly) { |
1032 | TypedefInfo Info; |
1033 | bool IsInAnonymousNamespace = false; |
1034 | populateInfo(I&: Info, D, C: FC, IsInAnonymousNamespace); |
1035 | if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) |
1036 | return {}; |
1037 | |
1038 | Info.DefLoc = Loc; |
1039 | const LangOptions &LO = D->getLangOpts(); |
1040 | Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO); |
1041 | Info.TypeDeclaration = getTypeAlias(Alias: D); |
1042 | Info.IsUsing = true; |
1043 | |
1044 | extractCommentFromDecl(D, Info); |
1045 | |
1046 | // Info is wrapped in its parent scope so is returned in the second position. |
1047 | return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(Child: std::move(Info))}; |
1048 | } |
1049 | |
1050 | std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>> |
1051 | emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc, |
1052 | bool PublicOnly) { |
1053 | EnumInfo Enum; |
1054 | bool IsInAnonymousNamespace = false; |
1055 | populateSymbolInfo(I&: Enum, D, C: FC, Loc, IsInAnonymousNamespace); |
1056 | |
1057 | if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D)) |
1058 | return {}; |
1059 | |
1060 | Enum.Scoped = D->isScoped(); |
1061 | if (D->isFixed()) { |
1062 | auto Name = D->getIntegerType().getAsString(); |
1063 | Enum.BaseType = TypeInfo(Name, Name); |
1064 | } |
1065 | parseEnumerators(I&: Enum, D); |
1066 | |
1067 | // Info is wrapped in its parent scope so is returned in the second position. |
1068 | return {nullptr, makeAndInsertIntoParent<EnumInfo &&>(Child: std::move(Enum))}; |
1069 | } |
1070 | |
1071 | } // namespace serialize |
1072 | } // namespace doc |
1073 | } // namespace clang |
1074 |
Definitions
- hashUSR
- getTemplateParameters
- getFunctionPrototype
- getTypeAlias
- getRecordPrototype
- getInfoRelativePath
- getInfoRelativePath
- ClangDocCommentVisitor
- ClangDocCommentVisitor
- parseComment
- visitTextComment
- visitInlineCommandComment
- visitHTMLStartTagComment
- visitHTMLEndTagComment
- visitBlockCommandComment
- visitParamCommandComment
- visitTParamCommandComment
- visitVerbatimBlockComment
- visitVerbatimBlockLineComment
- visitVerbatimLineComment
- isWhitespaceOnly
- getCommandName
- getSourceCode
- serialize
- serialize
- parseFullComment
- getUSRForDecl
- getTagDeclForType
- getRecordDeclForType
- getTypeInfoForType
- isPublic
- shouldSerializeInfo
- InsertChild
- InsertChild
- InsertChild
- InsertChild
- InsertChild
- makeAndInsertIntoParent
- getFinalAccessSpecifier
- parseFields
- parseEnumerators
- parseParameters
- parseBases
- populateParentNamespaces
- populateTemplateParameters
- convertTemplateArgToInfo
- populateInfo
- populateSymbolInfo
- populateFunctionInfo
- populateMemberTypeInfo
- populateMemberTypeInfo
- parseBases
- emitInfo
- emitInfo
- emitInfo
- emitInfo
- extractCommentFromDecl
- emitInfo
- emitInfo
Learn to use CMake with our Intro Training
Find out more