clang-tools 23.0.0git
Serialize.cpp
Go to the documentation of this file.
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
12#include "clang/AST/Attr.h"
13#include "clang/AST/Comment.h"
14#include "clang/AST/CommentVisitor.h"
15#include "clang/AST/DeclFriend.h"
16#include "clang/AST/ExprConcepts.h"
17#include "clang/AST/Mangle.h"
18#include "clang/Lex/Lexer.h"
19#include "clang/UnifiedSymbolResolution/USRGeneration.h"
20#include "llvm/ADT/StringExtras.h"
21#include "llvm/Support/SHA1.h"
22
23using clang::comments::FullComment;
24
25namespace clang {
26namespace doc {
27namespace serialize {
28
29namespace {
30static StringRef exprToString(const clang::Expr *E) {
31 clang::LangOptions Opts;
32 clang::PrintingPolicy Policy(Opts);
33 SmallString<16> Result;
34 llvm::raw_svector_ostream OS(Result);
35 E->printPretty(OS, nullptr, Policy);
36 return internString(Result);
37}
38} // namespace
39
40SymbolID hashUSR(llvm::StringRef USR) {
41 return llvm::SHA1::hash(arrayRefFromStringRef(USR));
42}
43
44void Serializer::getTemplateParameters(
45 const TemplateParameterList *TemplateParams, llvm::raw_ostream &Stream) {
46 Stream << "template <";
47
48 for (unsigned i = 0; i < TemplateParams->size(); ++i) {
49 if (i > 0)
50 Stream << ", ";
51
52 const NamedDecl *Param = TemplateParams->getParam(i);
53 if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Param)) {
54 if (TTP->wasDeclaredWithTypename())
55 Stream << "typename";
56 else
57 Stream << "class";
58 if (TTP->isParameterPack())
59 Stream << "...";
60 Stream << " " << TTP->getNameAsString();
61
62 // We need to also handle type constraints for code like:
63 // template <class T = void>
64 // class C {};
65 if (TTP->hasTypeConstraint()) {
66 Stream << " = ";
67 TTP->getTypeConstraint()->print(
68 Stream, TTP->getASTContext().getPrintingPolicy());
69 }
70 } else if (const auto *NTTP =
71 llvm::dyn_cast<NonTypeTemplateParmDecl>(Param)) {
72 NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy());
73 if (NTTP->isParameterPack())
74 Stream << "...";
75 Stream << " " << NTTP->getNameAsString();
76 } else if (const auto *TTPD =
77 llvm::dyn_cast<TemplateTemplateParmDecl>(Param)) {
78 Stream << "template <";
79 getTemplateParameters(TTPD->getTemplateParameters(), Stream);
80 Stream << "> class " << TTPD->getNameAsString();
81 }
82 }
83
84 Stream << "> ";
85}
86
87// Extract the full function prototype from a FunctionDecl including
88// Full Decl
89StringRef Serializer::getFunctionPrototype(const FunctionDecl *FuncDecl) {
90 llvm::SmallString<256> Result;
91 llvm::raw_svector_ostream Stream(Result);
92 const ASTContext &Ctx = FuncDecl->getASTContext();
93 const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl);
94 // If it's a templated function, handle the template parameters
95 if (const auto *TmplDecl = FuncDecl->getDescribedTemplate())
96 getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
97
98 // If it's a virtual method
99 if (Method && Method->isVirtual())
100 Stream << "virtual ";
101
102 // Print return type
103 FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy());
104
105 // Print function name
106 Stream << " " << FuncDecl->getNameAsString() << "(";
107
108 // Print parameter list with types, names, and default values
109 for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) {
110 if (I > 0)
111 Stream << ", ";
112 const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(I);
113 QualType ParamType = ParamDecl->getType();
114 ParamType.print(Stream, Ctx.getPrintingPolicy());
115
116 // Print parameter name if it has one
117 if (!ParamDecl->getName().empty())
118 Stream << " " << ParamDecl->getNameAsString();
119
120 // Print default argument if it exists
121 if (ParamDecl->hasDefaultArg() &&
122 !ParamDecl->hasUninstantiatedDefaultArg()) {
123 if (const Expr *DefaultArg = ParamDecl->getDefaultArg()) {
124 Stream << " = ";
125 DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy());
126 }
127 }
128 }
129
130 // If it is a variadic function, add '...'
131 if (FuncDecl->isVariadic()) {
132 if (FuncDecl->getNumParams() > 0)
133 Stream << ", ";
134 Stream << "...";
135 }
136
137 Stream << ")";
138
139 // If it's a const method, add 'const' qualifier
140 if (Method) {
141 if (Method->isDeleted())
142 Stream << " = delete";
143 if (Method->size_overridden_methods())
144 Stream << " override";
145 if (Method->hasAttr<clang::FinalAttr>())
146 Stream << " final";
147 if (Method->isConst())
148 Stream << " const";
149 if (Method->isPureVirtual())
150 Stream << " = 0";
151 }
152
153 if (auto ExceptionSpecType = FuncDecl->getExceptionSpecType())
154 Stream << " " << ExceptionSpecType;
155
156 return internString(Result);
157}
158
159StringRef Serializer::getTypeAlias(const TypeAliasDecl *Alias) {
160 llvm::SmallString<16> Result;
161 llvm::raw_svector_ostream Stream(Result);
162 const ASTContext &Ctx = Alias->getASTContext();
163 if (const auto *TmplDecl = Alias->getDescribedTemplate())
164 getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
165 Stream << "using " << Alias->getNameAsString() << " = ";
166 QualType Q = Alias->getUnderlyingType();
167 Q.print(Stream, Ctx.getPrintingPolicy());
168
169 return internString(Result);
170}
171
172// A function to extract the appropriate relative path for a given info's
173// documentation. The path returned is a composite of the parent namespaces.
174//
175// Example: Given the below, the directory path for class C info will be
176// <root>/A/B
177//
178// namespace A {
179// namespace B {
180//
181// class C {};
182//
183// }
184// }
185StringRef Serializer::getInfoRelativePath(
186 const llvm::SmallVectorImpl<doc::Reference> &Namespaces) {
187 llvm::SmallString<128> Path;
188 for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
189 llvm::sys::path::append(Path, R->Name);
190 return internString(Path);
191}
192
193StringRef Serializer::getInfoRelativePath(const Decl *D) {
194 llvm::SmallVector<Reference, 4> Namespaces;
195 // The third arg in populateParentNamespaces is a boolean passed by reference,
196 // its value is not relevant in here so it's not used anywhere besides the
197 // function call
198 bool B = true;
199 populateParentNamespaces(Namespaces, D, B);
200 return getInfoRelativePath(Namespaces);
201}
202
204 : public ConstCommentVisitor<ClangDocCommentVisitor> {
205public:
206 ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
207
208 void parseComment(const comments::Comment *C);
209
210 void visitTextComment(const TextComment *C);
211 void visitInlineCommandComment(const InlineCommandComment *C);
212 void visitHTMLStartTagComment(const HTMLStartTagComment *C);
213 void visitHTMLEndTagComment(const HTMLEndTagComment *C);
214 void visitBlockCommandComment(const BlockCommandComment *C);
215 void visitParamCommandComment(const ParamCommandComment *C);
216 void visitTParamCommandComment(const TParamCommandComment *C);
217 void visitVerbatimBlockComment(const VerbatimBlockComment *C);
218 void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
219 void visitVerbatimLineComment(const VerbatimLineComment *C);
220
221private:
222 StringRef getCommandName(unsigned CommandID) const;
223 bool isWhitespaceOnly(StringRef S) const;
224
225 CommentInfo &CurrentCI;
226};
227
228void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
229 CurrentCI.Kind = stringToCommentKind(C->getCommentKindName());
231
232 unsigned NumChildren = C->child_count();
233 if (NumChildren > 0) {
234 CommentInfo *ChildrenArray =
235 TransientArena.Allocate<CommentInfo>(NumChildren);
236 unsigned I = 0;
237 for (comments::Comment *Child :
238 llvm::make_range(C->child_begin(), C->child_end())) {
239 new (&ChildrenArray[I]) CommentInfo();
240 ClangDocCommentVisitor Visitor(ChildrenArray[I]);
241 Visitor.parseComment(Child);
242 I++;
243 }
244 CurrentCI.Children =
245 llvm::ArrayRef<CommentInfo>(ChildrenArray, NumChildren);
246 }
247}
248
250 if (!isWhitespaceOnly(C->getText()))
251 CurrentCI.Text = C->getText().trim();
252}
253
255 const InlineCommandComment *C) {
256 CurrentCI.Name = internString(getCommandName(C->getCommandID()));
257 llvm::SmallVector<StringRef> Args;
258 for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
259 Args.push_back(internString(C->getArgText(I).trim()));
260 if (!Args.empty()) {
261 StringRef *ArgsMem = TransientArena.Allocate<StringRef>(Args.size());
262 std::uninitialized_copy(Args.begin(), Args.end(), ArgsMem);
263 CurrentCI.Args = llvm::ArrayRef<StringRef>(ArgsMem, Args.size());
264 }
265}
266
268 const HTMLStartTagComment *C) {
269 CurrentCI.Name = internString(C->getTagName());
270 CurrentCI.SelfClosing = C->isSelfClosing();
271 llvm::SmallVector<StringRef> AttrKeys;
272 llvm::SmallVector<StringRef> AttrValues;
273 for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
274 const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
275 AttrKeys.push_back(internString(Attr.Name));
276 AttrValues.push_back(internString(Attr.Value));
277 }
278 if (!AttrKeys.empty()) {
279 StringRef *KeysMem = TransientArena.Allocate<StringRef>(AttrKeys.size());
280 std::uninitialized_copy(AttrKeys.begin(), AttrKeys.end(), KeysMem);
281 CurrentCI.AttrKeys = llvm::ArrayRef<StringRef>(KeysMem, AttrKeys.size());
282 }
283 if (!AttrValues.empty()) {
284 StringRef *ValuesMem =
285 TransientArena.Allocate<StringRef>(AttrValues.size());
286 std::uninitialized_copy(AttrValues.begin(), AttrValues.end(), ValuesMem);
287 CurrentCI.AttrValues =
288 llvm::ArrayRef<StringRef>(ValuesMem, AttrValues.size());
289 }
290}
291
293 const HTMLEndTagComment *C) {
294 CurrentCI.Name = internString(C->getTagName());
295 CurrentCI.SelfClosing = true;
296}
297
299 const BlockCommandComment *C) {
300 CurrentCI.Name = internString(getCommandName(C->getCommandID()));
301 llvm::SmallVector<StringRef> Args;
302 for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
303 Args.push_back(internString(C->getArgText(I).trim()));
304 if (!Args.empty()) {
305 StringRef *ArgsMem = TransientArena.Allocate<StringRef>(Args.size());
306 std::uninitialized_copy(Args.begin(), Args.end(), ArgsMem);
307 CurrentCI.Args = llvm::ArrayRef<StringRef>(ArgsMem, Args.size());
308 }
309}
310
312 const ParamCommandComment *C) {
313 CurrentCI.Direction = internString(
314 ParamCommandComment::getDirectionAsString(C->getDirection()));
315 CurrentCI.Explicit = C->isDirectionExplicit();
316 if (C->hasParamName())
317 CurrentCI.ParamName = internString(C->getParamNameAsWritten());
318}
319
321 const TParamCommandComment *C) {
322 if (C->hasParamName())
323 CurrentCI.ParamName = internString(C->getParamNameAsWritten());
324}
325
327 const VerbatimBlockComment *C) {
328 CurrentCI.Name = internString(getCommandName(C->getCommandID()));
329 CurrentCI.CloseName = internString(C->getCloseName());
330}
331
333 const VerbatimBlockLineComment *C) {
334 if (!isWhitespaceOnly(C->getText()))
335 CurrentCI.Text = internString(C->getText());
336}
337
339 const VerbatimLineComment *C) {
340 if (!isWhitespaceOnly(C->getText()))
341 CurrentCI.Text = internString(C->getText());
342}
343
344bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
345 return llvm::all_of(S, isspace);
346}
347
348StringRef ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
349 const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
350 if (Info)
351 return internString(Info->Name);
352 // TODO: Add parsing for \file command.
353 return "<not a builtin command>";
354}
355
356// Serializing functions.
357
358StringRef Serializer::getSourceCode(const Decl *D, const SourceRange &R) {
359 return internString(Lexer::getSourceText(
360 CharSourceRange::getTokenRange(R), D->getASTContext().getSourceManager(),
361 D->getASTContext().getLangOpts()));
362}
363
364template <typename T>
365static std::string serialize(T &I, DiagnosticsEngine &Diags) {
366 SmallString<2048> Buffer;
367 llvm::BitstreamWriter Stream(Buffer);
368 ClangDocBitcodeWriter Writer(Stream, Diags);
369 Writer.emitBlock(I);
370 return Buffer.str().str();
371}
372
373std::string serialize(OwnedPtr<Info> &I, DiagnosticsEngine &Diags) {
374 switch (I->IT) {
376 return serialize(*static_cast<NamespaceInfo *>(getPtr(I)), Diags);
378 return serialize(*static_cast<RecordInfo *>(getPtr(I)), Diags);
380 return serialize(*static_cast<EnumInfo *>(getPtr(I)), Diags);
382 return serialize(*static_cast<FunctionInfo *>(getPtr(I)), Diags);
384 return serialize(*static_cast<ConceptInfo *>(getPtr(I)), Diags);
386 return serialize(*static_cast<VarInfo *>(getPtr(I)), Diags);
390 return "";
391 }
392 llvm_unreachable("unhandled enumerator");
393}
394
395void Serializer::parseFullComment(const FullComment *C, CommentInfo &CI) {
396 ClangDocCommentVisitor Visitor(CI);
397 Visitor.parseComment(C);
398}
399
400SymbolID Serializer::getUSRForDecl(const Decl *D) {
401 llvm::SmallString<128> USR;
402 if (index::generateUSRForDecl(D, USR))
403 return SymbolID();
404 return hashUSR(USR);
405}
406
407TagDecl *Serializer::getTagDeclForType(const QualType &T) {
408 if (const TagDecl *D = T->getAsTagDecl())
409 return D->getDefinition();
410 return nullptr;
411}
412
413RecordDecl *Serializer::getRecordDeclForType(const QualType &T) {
414 if (const RecordDecl *D = T->getAsRecordDecl())
415 return D->getDefinition();
416 return nullptr;
417}
418
419TypeInfo Serializer::getTypeInfoForType(const QualType &T,
420 const PrintingPolicy &Policy) {
421 const TagDecl *TD = getTagDeclForType(T);
422 if (!TD) {
423 TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString(Policy)));
424 TI.IsBuiltIn = T->isBuiltinType();
425 TI.IsTemplate = T->isTemplateTypeParmType();
426 return TI;
427 }
428 InfoType IT;
429 if (isa<EnumDecl>(TD)) {
431 } else if (isa<RecordDecl>(TD)) {
433 } else {
435 }
436 Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
437 T.getAsString(Policy), getInfoRelativePath(TD));
438 TypeInfo TI = TypeInfo(R);
439 TI.IsBuiltIn = T->isBuiltinType();
440 TI.IsTemplate = T->isTemplateTypeParmType();
441 return TI;
442}
443
444bool Serializer::isPublic(const clang::AccessSpecifier AS,
445 const clang::Linkage Link) {
446 if (AS == clang::AccessSpecifier::AS_private)
447 return false;
448 if ((Link == clang::Linkage::Module) || (Link == clang::Linkage::External))
449 return true;
450 return false; // otherwise, linkage is some form of internal linkage
451}
452
453bool Serializer::shouldSerializeInfo(bool PublicOnly,
454 bool IsInAnonymousNamespace,
455 const NamedDecl *D) {
456 bool IsAnonymousNamespace = false;
457 if (const auto *N = dyn_cast<NamespaceDecl>(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().
470void Serializer::InsertChild(ScopeChildren &Scope, const NamespaceInfo &Info) {
471 Reference *R = allocatePtr<Reference>(TransientArena, Info.USR, Info.Name,
473 getInfoRelativePath(Info.Namespace));
474 Scope.Namespaces.push_back(*R);
475}
476
477void Serializer::InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
478 Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
479 Info.Name, getInfoRelativePath(Info.Namespace),
480 Info.MangledName);
481}
482
483void Serializer::InsertChild(ScopeChildren &Scope, EnumInfo Info) {
484 Scope.Enums.push_back(std::move(Info));
485}
486
487void Serializer::InsertChild(ScopeChildren &Scope, FunctionInfo Info) {
488 Scope.Functions.push_back(std::move(Info));
489}
490
491void Serializer::InsertChild(ScopeChildren &Scope, TypedefInfo Info) {
492 Scope.Typedefs.push_back(std::move(Info));
493}
494
495void Serializer::InsertChild(ScopeChildren &Scope, ConceptInfo Info) {
496 Scope.Concepts.push_back(std::move(Info));
497}
498
499void Serializer::InsertChild(ScopeChildren &Scope, VarInfo Info) {
500 Scope.Variables.push_back(std::move(Info));
501}
502
503// Creates a parent of the correct type for the given child and inserts it into
504// that parent.
505//
506// This is complicated by the fact that namespaces and records are inserted by
507// reference (constructing a "Reference" object with that namespace/record's
508// info), while everything else is inserted by moving it directly into the child
509// vectors.
510//
511// For namespaces and records, explicitly specify a const& template parameter
512// when invoking this function:
513// MakeAndInsertIntoParent<const Record&>(...);
514// Otherwise, specify an rvalue reference <EnumInfo&&> and move into the
515// parameter. Since each variant is used once, it's not worth having a more
516// elaborate system to automatically deduce this information.
517template <typename ChildType>
518OwnedPtr<Info> Serializer::makeAndInsertIntoParent(ChildType Child) {
519 if (Child.Namespace.empty()) {
520 // Insert into unnamed parent namespace.
521 auto ParentNS = allocatePtr<NamespaceInfo>();
522 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
523 return ParentNS;
524 }
525
526 switch (Child.Namespace[0].RefType) {
528 auto ParentNS = allocatePtr<NamespaceInfo>();
529 ParentNS->USR = Child.Namespace[0].USR;
530 InsertChild(ParentNS->Children, std::forward<ChildType>(Child));
531 return ParentNS;
532 }
533 case InfoType::IT_record: {
534 auto ParentRec = allocatePtr<RecordInfo>();
535 ParentRec->USR = Child.Namespace[0].USR;
536 InsertChild(ParentRec->Children, std::forward<ChildType>(Child));
537 return ParentRec;
538 }
546 break;
547 }
548 llvm_unreachable("Invalid reference type for parent namespace");
549}
550
551// There are two uses for this function.
552// 1) Getting the resulting mode of inheritance of a record.
553// Example: class A {}; class B : private A {}; class C : public B {};
554// It's explicit that C is publicly inherited from C and B is privately
555// inherited from A. It's not explicit but C is also privately inherited from
556// A. This is the AS that this function calculates. FirstAS is the
557// inheritance mode of `class C : B` and SecondAS is the inheritance mode of
558// `class B : A`.
559// 2) Getting the inheritance mode of an inherited attribute / method.
560// Example : class A { public: int M; }; class B : private A {};
561// Class B is inherited from class A, which has a public attribute. This
562// attribute is now part of the derived class B but it's not public. This
563// will be private because the inheritance is private. This is the AS that
564// this function calculates. FirstAS is the inheritance mode and SecondAS is
565// the AS of the attribute / method.
566AccessSpecifier Serializer::getFinalAccessSpecifier(AccessSpecifier FirstAS,
567 AccessSpecifier SecondAS) {
568 if (FirstAS == AccessSpecifier::AS_none ||
569 SecondAS == AccessSpecifier::AS_none)
570 return AccessSpecifier::AS_none;
571 if (FirstAS == AccessSpecifier::AS_private ||
572 SecondAS == AccessSpecifier::AS_private)
573 return AccessSpecifier::AS_private;
574 if (FirstAS == AccessSpecifier::AS_protected ||
575 SecondAS == AccessSpecifier::AS_protected)
576 return AccessSpecifier::AS_protected;
577 return AccessSpecifier::AS_public;
578}
579
580// The Access parameter is only provided when parsing the field of an inherited
581// record, the access specification of the field depends on the inheritance mode
582void Serializer::parseFields(RecordInfo &I, const RecordDecl *D,
583 bool PublicOnly, AccessSpecifier Access) {
584 for (const FieldDecl *F : D->fields()) {
585 if (!shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, F))
586 continue;
587 populateMemberTypeInfo(I, Access, F);
588 }
589 const auto *CxxRD = dyn_cast<CXXRecordDecl>(D);
590 if (!CxxRD)
591 return;
592 for (Decl *CxxDecl : CxxRD->decls()) {
593 auto *VD = dyn_cast<VarDecl>(CxxDecl);
594 if (!VD ||
595 !shouldSerializeInfo(PublicOnly, /*IsInAnonymousNamespace=*/false, VD))
596 continue;
597
598 if (VD->isStaticDataMember())
599 populateMemberTypeInfo(I, Access, VD, /*IsStatic=*/true);
600 }
601}
602
603void Serializer::parseEnumerators(EnumInfo &I, const EnumDecl *D) {
604 for (const EnumConstantDecl *E : D->enumerators()) {
605 std::string ValueExpr;
606 if (const Expr *InitExpr = E->getInitExpr())
607 ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
608 SmallString<16> ValueStr;
609 E->getInitVal().toString(ValueStr);
610 I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
611 ASTContext &Context = E->getASTContext();
612 if (RawComment *Comment =
613 E->getASTContext().getRawCommentForDeclNoCache(E)) {
614 Comment->setAttached();
615 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
616 EnumValueInfo &Member = I.Members.back();
617 Member.Description.emplace_back();
618 parseFullComment(Fc, Member.Description.back());
619 }
620 }
621 }
622}
623
624void Serializer::parseParameters(FunctionInfo &I, const FunctionDecl *D) {
625 auto &LO = D->getLangOpts();
626 for (const ParmVarDecl *P : D->parameters()) {
627 FieldTypeInfo &FieldInfo = I.Params.emplace_back(
628 getTypeInfoForType(P->getOriginalType(), LO), P->getNameAsString());
629 if (std::optional<StringRef> DefaultValue =
630 getSourceCode(D, P->getDefaultArgRange()))
631 FieldInfo.DefaultValue = *DefaultValue;
632 }
633}
634
635// TODO: Remove the serialization of Parents and VirtualParents, this
636// information is also extracted in the other definition of parseBases.
637void Serializer::parseBases(RecordInfo &I, const CXXRecordDecl *D) {
638 // Don't parse bases if this isn't a definition.
639 if (!D->isThisDeclarationADefinition())
640 return;
641
642 for (const CXXBaseSpecifier &B : D->bases()) {
643 if (B.isVirtual())
644 continue;
645 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
646 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
647 I.Parents.emplace_back(getUSRForDecl(D), B.getType().getAsString(),
648 InfoType::IT_record, B.getType().getAsString());
649 } else if (const RecordDecl *P = getRecordDeclForType(B.getType()))
650 I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
651 InfoType::IT_record, P->getQualifiedNameAsString(),
652 internString(getInfoRelativePath(P)));
653 else
654 I.Parents.emplace_back(SymbolID(), B.getType().getAsString());
655 }
656 for (const CXXBaseSpecifier &B : D->vbases()) {
657 if (const RecordDecl *P = getRecordDeclForType(B.getType()))
658 I.VirtualParents.emplace_back(
659 getUSRForDecl(P), P->getNameAsString(), InfoType::IT_record,
660 P->getQualifiedNameAsString(), internString(getInfoRelativePath(P)));
661 else
662 I.VirtualParents.emplace_back(SymbolID(), B.getType().getAsString());
663 }
664}
665
666template <typename T>
667void Serializer::populateParentNamespaces(
668 llvm::SmallVector<Reference, 4> &Namespaces, const T *D,
669 bool &IsInAnonymousNamespace) {
670 const DeclContext *DC = D->getDeclContext();
671 do {
672 if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
673 std::string Namespace;
674 if (N->isAnonymousNamespace()) {
675 Namespace = "@nonymous_namespace";
676 IsInAnonymousNamespace = true;
677 } else
678 Namespace = N->getNameAsString();
679 Namespaces.emplace_back(getUSRForDecl(N), Namespace,
681 N->getQualifiedNameAsString());
682 } else if (const auto *N = dyn_cast<RecordDecl>(DC))
683 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
685 N->getQualifiedNameAsString());
686 else if (const auto *N = dyn_cast<FunctionDecl>(DC))
687 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
689 N->getQualifiedNameAsString());
690 else if (const auto *N = dyn_cast<EnumDecl>(DC))
691 Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
692 InfoType::IT_enum, N->getQualifiedNameAsString());
693 } while ((DC = DC->getParent()));
694 // The global namespace should be added to the list of namespaces if the decl
695 // corresponds to a Record and if it doesn't have any namespace (because this
696 // means it's in the global namespace). Also if its outermost namespace is a
697 // record because that record matches the previous condition mentioned.
698 if ((Namespaces.empty() && isa<RecordDecl>(D)) ||
699 (!Namespaces.empty() && Namespaces.back().RefType == InfoType::IT_record))
700 Namespaces.emplace_back(SymbolID(), "GlobalNamespace",
702}
703
704void Serializer::populateTemplateParameters(
705 std::optional<TemplateInfo> &TemplateInfo, const clang::Decl *D) {
706 if (const TemplateParameterList *ParamList =
707 D->getDescribedTemplateParams()) {
708 if (!TemplateInfo) {
709 TemplateInfo.emplace();
710 }
711 for (const NamedDecl *ND : *ParamList) {
712 TemplateInfo->Params.emplace_back(
713 getSourceCode(ND, ND->getSourceRange()));
714 }
715 }
716}
717
718TemplateParamInfo
719Serializer::convertTemplateArgToInfo(const clang::Decl *D,
720 const TemplateArgument &Arg) {
721 // The TemplateArgument's pretty printing handles all the normal cases
722 // well enough for our requirements.
723 std::string Str;
724 llvm::raw_string_ostream Stream(Str);
725 Arg.print(PrintingPolicy(D->getLangOpts()), Stream, false);
726 return TemplateParamInfo(Str);
727}
728
729// Check if the DeclKind is one for which we support contextual relationships.
730// There might be other ContextDecls, like blocks, that we currently don't
731// handle at all.
732bool Serializer::isSupportedContext(Decl::Kind DeclKind) {
733 switch (DeclKind) {
734 case Decl::Kind::Record:
735 case Decl::Kind::CXXRecord:
736 case Decl::Kind::ClassTemplateSpecialization:
737 case Decl::Kind::ClassTemplatePartialSpecialization:
738 case Decl::Kind::Namespace:
739 return true;
740 default:
741 return false;
742 }
743}
744
745void Serializer::findParent(Info &I, const Decl *D) {
746 assert(D && "Invalid Decl");
747
748 // Only walk up contexts if D is a record or namespace.
749 if (!isSupportedContext(D->getKind()))
750 return;
751
752 const DeclContext *ParentCtx = dyn_cast<DeclContext>(D)->getLexicalParent();
753 while (ParentCtx) {
754 if (isSupportedContext(ParentCtx->getDeclKind())) {
755 // Break when we reach the first record or namespace.
756 I.ParentUSR = getUSRForDecl(dyn_cast<Decl>(ParentCtx));
757 break;
758 }
759 ParentCtx = ParentCtx->getParent();
760 }
761}
762
763template <typename T>
764void Serializer::populateInfo(Info &I, const T *D, const FullComment *C,
765 bool &IsInAnonymousNamespace) {
766 I.USR = getUSRForDecl(D);
767 findParent(I, D);
768
769 if (auto ConversionDecl = dyn_cast_or_null<CXXConversionDecl>(D);
770 ConversionDecl && ConversionDecl->getConversionType()
771 .getTypePtr()
772 ->isTemplateTypeParmType())
773 I.Name = internString("operator " +
774 ConversionDecl->getConversionType().getAsString());
775 else
776 I.Name = internString(D->getNameAsString());
777 populateParentNamespaces(I.Namespace, D, IsInAnonymousNamespace);
778 if (C) {
779 I.Description.emplace_back();
780 parseFullComment(C, I.Description.back());
781 }
782}
783
784template <typename T>
785void Serializer::populateSymbolInfo(SymbolInfo &I, const T *D,
786 const FullComment *C, Location Loc,
787 bool &IsInAnonymousNamespace) {
788 populateInfo(I, D, C, IsInAnonymousNamespace);
789 if (D->isThisDeclarationADefinition())
790 I.DefLoc = Loc;
791 else
792 I.Loc.emplace_back(Loc);
793
794 auto *Mangler = ItaniumMangleContext::create(
795 D->getASTContext(), D->getASTContext().getDiagnostics());
796 std::string MangledName;
797 llvm::raw_string_ostream MangledStream(MangledName);
798 if (auto *CXXD = dyn_cast<CXXRecordDecl>(D))
799 Mangler->mangleCXXVTable(CXXD, MangledStream);
800 else
801 MangledStream << D->getNameAsString();
802 // A 250 length limit was chosen since 255 is a common limit across
803 // different filesystems, with a 5 character buffer for file extensions.
804 if (MangledName.size() > 250) {
805 auto SymbolID = llvm::toStringRef(llvm::toHex(I.USR)).str();
806 I.MangledName =
807 internString(MangledName.substr(0, 250 - SymbolID.size()) + SymbolID);
808 } else
809 I.MangledName = internString(MangledName);
810 delete Mangler;
811}
812
813void Serializer::handleCompoundConstraints(
814 const Expr *Constraint, OwningVec<ConstraintInfo> &ConstraintInfos) {
815 if (Constraint->getStmtClass() == Stmt::ParenExprClass) {
816 handleCompoundConstraints(dyn_cast<ParenExpr>(Constraint)->getSubExpr(),
817 ConstraintInfos);
818 } else if (Constraint->getStmtClass() == Stmt::BinaryOperatorClass) {
819 auto *BinaryOpExpr = dyn_cast<BinaryOperator>(Constraint);
820 handleCompoundConstraints(BinaryOpExpr->getLHS(), ConstraintInfos);
821 handleCompoundConstraints(BinaryOpExpr->getRHS(), ConstraintInfos);
822 } else if (Constraint->getStmtClass() ==
823 Stmt::ConceptSpecializationExprClass) {
824 auto *Concept = dyn_cast<ConceptSpecializationExpr>(Constraint);
825 ConstraintInfo CI(getUSRForDecl(Concept->getNamedConcept()),
826 Concept->getNamedConcept()->getNameAsString());
827 CI.ConstraintExpr = internString(exprToString(Concept));
828 ConstraintInfos.push_back(CI);
829 }
830}
831
832void Serializer::populateConstraints(TemplateInfo &I, const TemplateDecl *D) {
833 if (!D || !D->hasAssociatedConstraints())
834 return;
835
836 SmallVector<AssociatedConstraint> AssociatedConstraints;
837 D->getAssociatedConstraints(AssociatedConstraints);
838 for (const auto &Constraint : AssociatedConstraints) {
839 if (!Constraint)
840 continue;
841
842 // TODO: Investigate if atomic constraints need to be handled specifically.
843 if (const auto *ConstraintExpr =
844 dyn_cast_or_null<ConceptSpecializationExpr>(
845 Constraint.ConstraintExpr)) {
846 ConstraintInfo CI(getUSRForDecl(ConstraintExpr->getNamedConcept()),
847 ConstraintExpr->getNamedConcept()->getNameAsString());
848 CI.ConstraintExpr = internString(exprToString(ConstraintExpr));
849 I.Constraints.push_back(std::move(CI));
850 } else {
851 handleCompoundConstraints(Constraint.ConstraintExpr, I.Constraints);
852 }
853 }
854}
855
856void Serializer::populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
857 const FullComment *FC, Location Loc,
858 bool &IsInAnonymousNamespace) {
859 populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
860 auto &LO = D->getLangOpts();
861 I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
862 I.Prototype = getFunctionPrototype(D);
863 parseParameters(I, D);
864 I.IsStatic = D->isStatic();
865
866 populateTemplateParameters(I.Template, D);
867 if (I.Template)
868 populateConstraints(I.Template.value(), D->getDescribedFunctionTemplate());
869
870 // Handle function template specializations.
871 if (const FunctionTemplateSpecializationInfo *FTSI =
872 D->getTemplateSpecializationInfo()) {
873 if (!I.Template)
874 I.Template.emplace();
875 I.Template->Specialization.emplace();
876 auto &Specialization = *I.Template->Specialization;
877
878 Specialization.SpecializationOf = getUSRForDecl(FTSI->getTemplate());
879
880 // Template parameters to the specialization.
881 if (FTSI->TemplateArguments) {
882 for (const TemplateArgument &Arg : FTSI->TemplateArguments->asArray()) {
883 Specialization.Params.push_back(convertTemplateArgToInfo(D, Arg));
884 }
885 }
886 }
887}
888
889// TODO: Rename this, since this doesn't populate anything besides comments and
890// isn't exclusive to members
891template <typename T>
892void Serializer::populateMemberTypeInfo(T &I, const Decl *D) {
893 assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
894
895 ASTContext &Context = D->getASTContext();
896 // TODO investigate whether we can use ASTContext::getCommentForDecl instead
897 // of this logic. See also similar code in Mapper.cpp.
898 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
899 if (!Comment)
900 return;
901
902 Comment->setAttached();
903 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
904 I.Description.emplace_back();
905 parseFullComment(Fc, I.Description.back());
906 }
907}
908
909void Serializer::populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
910 const DeclaratorDecl *D,
911 bool IsStatic) {
912 // Use getAccessUnsafe so that we just get the default AS_none if it's not
913 // valid, as opposed to an assert.
914 MemberTypeInfo &NewMember = I.Members.emplace_back(
915 getTypeInfoForType(D->getTypeSourceInfo()->getType(), D->getLangOpts()),
916 D->getNameAsString(),
917 getFinalAccessSpecifier(Access, D->getAccessUnsafe()), IsStatic);
918 populateMemberTypeInfo(NewMember, D);
919}
920
921void Serializer::parseBases(RecordInfo &I, const CXXRecordDecl *D,
922 bool IsFileInRootDir, bool PublicOnly,
923 bool IsParent, AccessSpecifier ParentAccess) {
924 // Don't parse bases if this isn't a definition.
925 if (!D->isThisDeclarationADefinition())
926 return;
927 for (const CXXBaseSpecifier &B : D->bases()) {
928 if (const auto *Base = B.getType()->getAsCXXRecordDecl()) {
929 if (Base->isCompleteDefinition()) {
930 // Initialized without USR and name, this will be set in the following
931 // if-else stmt.
932 BaseRecordInfo BI(
933 {}, "", internString(getInfoRelativePath(Base)), B.isVirtual(),
934 getFinalAccessSpecifier(ParentAccess, B.getAccessSpecifier()),
935 IsParent);
936 if (const auto *Ty = B.getType()->getAs<TemplateSpecializationType>()) {
937 const TemplateDecl *D = Ty->getTemplateName().getAsTemplateDecl();
938 BI.USR = getUSRForDecl(D);
939 BI.Name = internString(B.getType().getAsString());
940 } else {
941 BI.USR = getUSRForDecl(Base);
942 BI.Name = internString(Base->getNameAsString());
943 }
944 parseFields(BI, Base, PublicOnly, BI.Access);
945 for (const auto &Decl : Base->decls())
946 if (const auto *MD = dyn_cast<CXXMethodDecl>(Decl)) {
947 // Don't serialize private methods
948 if (MD->getAccessUnsafe() == AccessSpecifier::AS_private ||
949 !MD->isUserProvided())
950 continue;
951 FunctionInfo FI;
952 FI.IsMethod = true;
953 FI.IsStatic = MD->isStatic();
954 // The seventh arg in populateFunctionInfo is a boolean passed by
955 // reference, its value is not relevant in here so it's not used
956 // anywhere besides the function call.
957 bool IsInAnonymousNamespace;
958 populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},
959 IsInAnonymousNamespace);
960 FI.Access =
961 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
962 BI.Children.Functions.emplace_back(std::move(FI));
963 }
964 I.Bases.emplace_back(std::move(BI));
965 // Call this function recursively to get the inherited classes of
966 // this base; these new bases will also get stored in the original
967 // RecordInfo: I.
968 parseBases(I, Base, IsFileInRootDir, PublicOnly, false,
969 I.Bases.back().Access);
970 }
971 }
972 }
973}
974
975std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
976Serializer::emitInfo(const NamespaceDecl *D, const FullComment *FC,
977 Location Loc, bool PublicOnly) {
978 auto NSI = allocatePtr<NamespaceInfo>();
979 bool IsInAnonymousNamespace = false;
980 populateInfo(*NSI, D, FC, IsInAnonymousNamespace);
981 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
982 return {};
983
984 NSI->Name = D->isAnonymousNamespace() ? "@nonymous_namespace" : NSI->Name;
985 NSI->Path = getInfoRelativePath(NSI->Namespace);
986 if (NSI->Namespace.empty() && NSI->USR == SymbolID())
987 return {OwnedPtr<Info>{std::move(NSI)}, nullptr};
988
989 // Namespaces are inserted into the parent by reference, so we need to return
990 // both the parent and the record itself.
991 return {std::move(NSI), makeAndInsertIntoParent<const NamespaceInfo &>(*NSI)};
992}
993
994void Serializer::parseFriends(RecordInfo &RI, const CXXRecordDecl *D) {
995 if (!D->hasDefinition() || !D->hasFriends())
996 return;
997
998 for (const FriendDecl *FD : D->friends()) {
999 if (FD->isUnsupportedFriend())
1000 continue;
1001
1002 FriendInfo F(InfoType::IT_friend, getUSRForDecl(FD));
1003 const auto *ActualDecl = FD->getFriendDecl();
1004 if (!ActualDecl) {
1005 const auto *FriendTypeInfo = FD->getFriendType();
1006 if (!FriendTypeInfo)
1007 continue;
1008 ActualDecl = FriendTypeInfo->getType()->getAsCXXRecordDecl();
1009
1010 if (!ActualDecl)
1011 continue;
1012 F.IsClass = true;
1013 }
1014
1015 if (const auto *ActualTD = dyn_cast_or_null<TemplateDecl>(ActualDecl)) {
1016 if (isa<RecordDecl>(ActualTD->getTemplatedDecl()))
1017 F.IsClass = true;
1018 F.Template.emplace();
1019 for (const auto *Param : ActualTD->getTemplateParameters()->asArray())
1020 F.Template->Params.emplace_back(
1021 getSourceCode(Param, Param->getSourceRange()));
1022 ActualDecl = ActualTD->getTemplatedDecl();
1023 }
1024
1025 if (auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(ActualDecl)) {
1026 FunctionInfo TempInfo;
1027 parseParameters(TempInfo, FuncDecl);
1028 F.Params = allocateArray<FieldTypeInfo>(TempInfo.Params, TransientArena);
1029 F.ReturnType = getTypeInfoForType(FuncDecl->getReturnType(),
1030 FuncDecl->getLangOpts());
1031 }
1032
1033 F.Ref =
1034 Reference(getUSRForDecl(ActualDecl), ActualDecl->getNameAsString(),
1035 InfoType::IT_default, ActualDecl->getQualifiedNameAsString(),
1036 getInfoRelativePath(ActualDecl));
1037
1038 populateMemberTypeInfo(F, ActualDecl);
1039 RI.Friends.push_back(std::move(F));
1040 }
1041}
1042
1043std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1044Serializer::emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
1045 bool PublicOnly) {
1046
1047 auto RI = allocatePtr<RecordInfo>();
1048 bool IsInAnonymousNamespace = false;
1049
1050 populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);
1051 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1052 return {};
1053
1054 RI->TagType = D->getTagKind();
1055 parseFields(*RI, D, PublicOnly);
1056
1057 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
1058 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
1059 RI->Name = internString(TD->getNameAsString());
1060 RI->IsTypeDef = true;
1061 }
1062 // TODO: remove first call to parseBases, that function should be deleted
1063 parseBases(*RI, C);
1064 parseBases(*RI, C, /*IsFileInRootDir=*/true, PublicOnly, /*IsParent=*/true);
1065 parseFriends(*RI, C);
1066 }
1067 RI->Path = internString(getInfoRelativePath(RI->Namespace));
1068
1069 populateTemplateParameters(RI->Template, D);
1070 if (RI->Template)
1071 populateConstraints(RI->Template.value(), D->getDescribedTemplate());
1072
1073 // Full and partial specializations.
1074 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
1075 if (!RI->Template)
1076 RI->Template.emplace();
1077 RI->Template->Specialization.emplace();
1078 auto &Specialization = *RI->Template->Specialization;
1079
1080 // What this is a specialization of.
1081 auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
1082 if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
1083 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
1084 else if (auto *SpecTD =
1085 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
1086 Specialization.SpecializationOf = getUSRForDecl(SpecTD);
1087
1088 // Parameters to the specialization. For partial specializations, get the
1089 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
1090 // because the non-explicit template parameters will have generated internal
1091 // placeholder names rather than the names the user typed that match the
1092 // template parameters.
1093 if (const ClassTemplatePartialSpecializationDecl *CTPSD =
1094 dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
1095 if (const ASTTemplateArgumentListInfo *AsWritten =
1096 CTPSD->getTemplateArgsAsWritten()) {
1097 for (unsigned Idx = 0; Idx < AsWritten->getNumTemplateArgs(); Idx++) {
1098 Specialization.Params.emplace_back(
1099 getSourceCode(D, (*AsWritten)[Idx].getSourceRange()));
1100 }
1101 }
1102 } else {
1103 for (const TemplateArgument &Arg : CTSD->getTemplateArgs().asArray()) {
1104 Specialization.Params.push_back(convertTemplateArgToInfo(D, Arg));
1105 }
1106 }
1107 }
1108
1109 // Records are inserted into the parent by reference, so we need to return
1110 // both the parent and the record itself.
1111 auto Parent = makeAndInsertIntoParent<const RecordInfo &>(*RI);
1112 return {std::move(RI), std::move(Parent)};
1113}
1114
1115std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1116Serializer::emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc,
1117 bool PublicOnly) {
1118 FunctionInfo Func;
1119 bool IsInAnonymousNamespace = false;
1120 populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
1121 Func.Access = clang::AccessSpecifier::AS_none;
1122 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1123 return {};
1124
1125 // Info is wrapped in its parent scope so is returned in the second position.
1126 return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
1127}
1128
1129std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1130Serializer::emitInfo(const CXXMethodDecl *D, const FullComment *FC,
1131 Location Loc, bool PublicOnly) {
1132 FunctionInfo Func;
1133 bool IsInAnonymousNamespace = false;
1134 populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
1135 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1136 return {};
1137
1138 Func.IsMethod = true;
1139 Func.IsStatic = D->isStatic();
1140
1141 const NamedDecl *Parent = nullptr;
1142 if (const auto *SD =
1143 dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()))
1144 Parent = SD->getSpecializedTemplate();
1145 else
1146 Parent = D->getParent();
1147
1148 SymbolID ParentUSR = getUSRForDecl(Parent);
1149 Func.Parent =
1150 Reference{ParentUSR, Parent->getNameAsString(), InfoType::IT_record,
1151 Parent->getQualifiedNameAsString()};
1152 Func.Access = D->getAccess();
1153
1154 // Info is wrapped in its parent scope so is returned in the second position.
1155 return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
1156}
1157
1158void Serializer::extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
1159 assert(D && "Invalid Decl when extracting comment");
1160 ASTContext &Context = D->getASTContext();
1161 RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
1162 if (!Comment)
1163 return;
1164
1165 Comment->setAttached();
1166 if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
1167 Info.Description.emplace_back();
1168 parseFullComment(Fc, Info.Description.back());
1169 }
1170}
1171
1172std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1173Serializer::emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
1174 bool PublicOnly) {
1176 bool IsInAnonymousNamespace = false;
1177 populateInfo(Info, D, FC, IsInAnonymousNamespace);
1178
1179 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1180 return {};
1181
1182 Info.DefLoc = Loc;
1183 auto &LO = D->getLangOpts();
1184 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1185 populateTemplateParameters(Info.Template, D);
1186 if (Info.Template)
1187 populateConstraints(Info.Template.value(), D->getDescribedTemplate());
1188
1189 if (Info.Underlying.Type.Name.empty()) {
1190 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
1191 // The record serializer explicitly checks for this syntax and constructs
1192 // a record with that name, so we don't want to emit a duplicate here.
1193 return {};
1194 }
1195 Info.IsUsing = false;
1196 extractCommentFromDecl(D, Info);
1197
1198 // Info is wrapped in its parent scope so is returned in the second position.
1199 return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
1200}
1201
1202// A type alias is a C++ "using" declaration for a type. It gets mapped to a
1203// TypedefInfo with the IsUsing flag set.
1204std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1205Serializer::emitInfo(const TypeAliasDecl *D, const FullComment *FC,
1206 Location Loc, bool PublicOnly) {
1208 bool IsInAnonymousNamespace = false;
1209 populateInfo(Info, D, FC, IsInAnonymousNamespace);
1210 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1211 return {};
1212
1213 Info.DefLoc = Loc;
1214 const LangOptions &LO = D->getLangOpts();
1215 Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1216 Info.TypeDeclaration = getTypeAlias(D);
1217 Info.IsUsing = true;
1218 populateTemplateParameters(Info.Template, D);
1219 if (Info.Template)
1220 populateConstraints(Info.Template.value(), D->getDescribedAliasTemplate());
1221
1222 extractCommentFromDecl(D, Info);
1223
1224 // Info is wrapped in its parent scope so is returned in the second position.
1225 return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
1226}
1227
1228std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1229Serializer::emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,
1230 bool PublicOnly) {
1231 EnumInfo Enum;
1232 bool IsInAnonymousNamespace = false;
1233 populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace);
1234
1235 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1236 return {};
1237
1238 Enum.Scoped = D->isScoped();
1239 if (const TypeSourceInfo *TSI = D->getIntegerTypeSourceInfo()) {
1240 auto Name = TSI->getType().getAsString();
1241 Enum.BaseType = TypeInfo(Name, Name);
1242 }
1243 parseEnumerators(Enum, D);
1244
1245 // Info is wrapped in its parent scope so is returned in the second position.
1246 return {nullptr, makeAndInsertIntoParent<EnumInfo &&>(std::move(Enum))};
1247}
1248
1249std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1250Serializer::emitInfo(const ConceptDecl *D, const FullComment *FC,
1251 const Location &Loc, bool PublicOnly) {
1252 ConceptInfo Concept;
1253
1254 bool IsInAnonymousNamespace = false;
1255 populateInfo(Concept, D, FC, IsInAnonymousNamespace);
1256 Concept.IsType = D->isTypeConcept();
1257 Concept.DefLoc = Loc;
1258 Concept.ConstraintExpression = exprToString(D->getConstraintExpr());
1259
1260 if (auto *ConceptParams = D->getTemplateParameters()) {
1261 for (const auto *Param : ConceptParams->asArray()) {
1262 Concept.Template.Params.emplace_back(
1263 getSourceCode(Param, Param->getSourceRange()));
1264 }
1265 }
1266
1267 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1268 return {};
1269
1270 return {nullptr, makeAndInsertIntoParent<ConceptInfo &&>(std::move(Concept))};
1271}
1272
1273std::pair<OwnedPtr<Info>, OwnedPtr<Info>>
1274Serializer::emitInfo(const VarDecl *D, const FullComment *FC,
1275 const Location &Loc, bool PublicOnly) {
1276 VarInfo Var;
1277 bool IsInAnonymousNamespace = false;
1278 populateSymbolInfo(Var, D, FC, Loc, IsInAnonymousNamespace);
1279 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1280 return {};
1281
1282 if (D->getStorageClass() == StorageClass::SC_Static)
1283 Var.IsStatic = true;
1284 Var.Type =
1285 getTypeInfoForType(D->getType(), D->getASTContext().getPrintingPolicy());
1286
1287 if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
1288 return {};
1289
1290 return {nullptr, makeAndInsertIntoParent<VarInfo &&>(std::move(Var))};
1291}
1292
1293} // namespace serialize
1294} // namespace doc
1295} // namespace clang
static llvm::cl::opt< bool > PublicOnly("public", llvm::cl::desc("Document only public declarations."), llvm::cl::init(false), llvm::cl::cat(ClangDocCategory))
clang::find_all_symbols::SymbolInfo SymbolInfo
void emitBlock(const NamespaceInfo &I)
void visitHTMLEndTagComment(const HTMLEndTagComment *C)
void visitVerbatimLineComment(const VerbatimLineComment *C)
void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C)
void visitHTMLStartTagComment(const HTMLStartTagComment *C)
void visitTParamCommandComment(const TParamCommandComment *C)
void visitVerbatimBlockComment(const VerbatimBlockComment *C)
void visitParamCommandComment(const ParamCommandComment *C)
void visitInlineCommandComment(const InlineCommandComment *C)
void parseComment(const comments::Comment *C)
void visitTextComment(const TextComment *C)
void visitBlockCommandComment(const BlockCommandComment *C)
std::pair< OwnedPtr< Info >, OwnedPtr< Info > > emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc, bool PublicOnly)
@ Info
An information message.
Definition Protocol.h:738
std::string Path
A typedef to represent a file path.
Definition Path.h:26
@ Alias
This declaration is an alias that was referred to.
Definition FindTarget.h:112
static std::string serialize(T &I, DiagnosticsEngine &Diags)
SymbolID hashUSR(llvm::StringRef USR)
Definition Serialize.cpp:40
std::unique_ptr< T > OwnedPtr
llvm::ArrayRef< T > allocateArray(llvm::ArrayRef< T > V, llvm::BumpPtrAllocator &Alloc)
T * getPtr(const OwnedPtr< T > &O)
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for MD output.")
CommentKind stringToCommentKind(llvm::StringRef KindStr)
thread_local llvm::BumpPtrAllocator TransientArena
StringRef internString(const Twine &T)
std::vector< T > OwningVec
OwnedPtr< T > allocatePtr(Args &&...args)
std::array< uint8_t, 20 > SymbolID
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::ArrayRef< CommentInfo > Children
A base struct for Infos.
OwningVec< CommentInfo > Description
llvm::SmallVector< Reference, 4 > Namespace
std::optional< TemplateInfo > Template
OwningVec< FriendInfo > Friends
static constexpr const char FuncDecl[]