Skip to content

Commit b7e8fc4

Browse files
ilovepiPeterChou1
andcommitted
[clang-doc] Update serializer for improved template handling
This patch updates Serialize.cpp to serialize more data about C++ templates, which are supported by the new mustache HTML template. Split from #133161. Co-authored-by: Peter Chou <[email protected]>
1 parent aecb686 commit b7e8fc4

File tree

2 files changed

+209
-8
lines changed

2 files changed

+209
-8
lines changed

clang-tools-extra/clang-doc/Representation.h

+3
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,9 @@ struct FunctionInfo : public SymbolInfo {
363363
// specializations.
364364
SmallString<16> FullName;
365365

366+
// Function Prototype
367+
SmallString<256> Prototype;
368+
366369
// When present, this function is a template or specialization.
367370
std::optional<TemplateInfo> Template;
368371
};

clang-tools-extra/clang-doc/Serialize.cpp

+206-8
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
#include "Serialize.h"
1010
#include "BitcodeWriter.h"
11+
#include "clang/AST/Attr.h"
1112
#include "clang/AST/Comment.h"
1213
#include "clang/Index/USRGeneration.h"
1314
#include "clang/Lex/Lexer.h"
14-
#include "llvm/ADT/Hashing.h"
1515
#include "llvm/ADT/StringExtras.h"
1616
#include "llvm/Support/SHA1.h"
1717

@@ -35,6 +35,180 @@ static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
3535
const DeclaratorDecl *D,
3636
bool IsStatic = false);
3737

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(i);
47+
if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Param)) {
48+
if (TTP->wasDeclaredWithTypename())
49+
Stream << "typename";
50+
else
51+
Stream << "class";
52+
if (TTP->isParameterPack())
53+
Stream << "...";
54+
Stream << " " << TTP->getNameAsString();
55+
} else if (const auto *NTTP =
56+
llvm::dyn_cast<NonTypeTemplateParmDecl>(Param)) {
57+
NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy());
58+
if (NTTP->isParameterPack())
59+
Stream << "...";
60+
Stream << " " << NTTP->getNameAsString();
61+
} else if (const auto *TTPD =
62+
llvm::dyn_cast<TemplateTemplateParmDecl>(Param)) {
63+
Stream << "template <";
64+
getTemplateParameters(TTPD->getTemplateParameters(), Stream);
65+
Stream << "> class " << TTPD->getNameAsString();
66+
}
67+
}
68+
69+
Stream << "> ";
70+
}
71+
72+
// Extract the full function prototype from a FunctionDecl including
73+
// Full Decl
74+
static llvm::SmallString<256>
75+
getFunctionPrototype(const FunctionDecl *FuncDecl) {
76+
llvm::SmallString<256> Result;
77+
llvm::raw_svector_ostream Stream(Result);
78+
const ASTContext &Ctx = FuncDecl->getASTContext();
79+
const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl);
80+
// If it's a templated function, handle the template parameters
81+
if (const auto *TmplDecl = FuncDecl->getDescribedTemplate())
82+
getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
83+
84+
// If it's a virtual method
85+
if (Method && Method->isVirtual())
86+
Stream << "virtual ";
87+
88+
// Print return type
89+
FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy());
90+
91+
// Print function name
92+
Stream << " " << FuncDecl->getNameAsString() << "(";
93+
94+
// Print parameter list with types, names, and default values
95+
for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) {
96+
if (I > 0)
97+
Stream << ", ";
98+
const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(I);
99+
QualType ParamType = ParamDecl->getType();
100+
ParamType.print(Stream, Ctx.getPrintingPolicy());
101+
102+
// Print parameter name if it has one
103+
if (!ParamDecl->getName().empty())
104+
Stream << " " << ParamDecl->getNameAsString();
105+
106+
// Print default argument if it exists
107+
if (ParamDecl->hasDefaultArg()) {
108+
const Expr *DefaultArg = ParamDecl->getDefaultArg();
109+
if (DefaultArg) {
110+
Stream << " = ";
111+
DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy());
112+
}
113+
}
114+
}
115+
116+
// If it is a variadic function, add '...'
117+
if (FuncDecl->isVariadic()) {
118+
if (FuncDecl->getNumParams() > 0)
119+
Stream << ", ";
120+
Stream << "...";
121+
}
122+
123+
Stream << ")";
124+
125+
// If it's a const method, add 'const' qualifier
126+
if (Method) {
127+
if (Method->size_overridden_methods())
128+
Stream << " override";
129+
if (Method->hasAttr<clang::FinalAttr>())
130+
Stream << " final";
131+
if (Method->isConst())
132+
Stream << " const";
133+
if (Method->isPureVirtual())
134+
Stream << " = 0";
135+
}
136+
return Result; // Convert SmallString to std::string for return
137+
}
138+
139+
static llvm::SmallString<16> getTypeDefDecl(const TypedefDecl *TypeDef) {
140+
llvm::SmallString<16> Result;
141+
llvm::raw_svector_ostream Stream(Result);
142+
const ASTContext &Ctx = TypeDef->getASTContext();
143+
Stream << "typedef ";
144+
QualType Q = TypeDef->getUnderlyingType();
145+
Q.print(Stream, Ctx.getPrintingPolicy());
146+
Stream << " " << TypeDef->getNameAsString();
147+
return Result;
148+
}
149+
150+
static llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) {
151+
llvm::SmallString<16> Result;
152+
llvm::raw_svector_ostream Stream(Result);
153+
const ASTContext &Ctx = Alias->getASTContext();
154+
if (const auto *TmplDecl = Alias->getDescribedTemplate())
155+
getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
156+
Stream << "using " << Alias->getNameAsString() << " = ";
157+
QualType Q = Alias->getUnderlyingType();
158+
Q.print(Stream, Ctx.getPrintingPolicy());
159+
160+
return Result;
161+
}
162+
163+
// extract full syntax for record declaration
164+
static llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
165+
llvm::SmallString<16> Result;
166+
LangOptions LangOpts;
167+
PrintingPolicy Policy(LangOpts);
168+
Policy.SuppressTagKeyword = false;
169+
Policy.FullyQualifiedName = true;
170+
Policy.IncludeNewlines = false;
171+
llvm::raw_svector_ostream OS(Result);
172+
if (const auto *TD = CXXRD->getDescribedClassTemplate()) {
173+
OS << "template <";
174+
bool FirstParam = true;
175+
for (const auto *Param : *TD->getTemplateParameters()) {
176+
if (!FirstParam)
177+
OS << ", ";
178+
Param->print(OS, Policy);
179+
FirstParam = false;
180+
}
181+
OS << ">\n";
182+
}
183+
184+
if (CXXRD->isStruct())
185+
OS << "struct ";
186+
else if (CXXRD->isClass())
187+
OS << "class ";
188+
else if (CXXRD->isUnion())
189+
OS << "union ";
190+
191+
OS << CXXRD->getNameAsString();
192+
193+
// We need to make sure we have a good enough declaration to check. In the
194+
// case where the class is a forward declaration, we'll fail assertions in
195+
// DeclCXX.
196+
if (CXXRD->isCompleteDefinition() && CXXRD->getNumBases() > 0) {
197+
OS << " : ";
198+
bool FirstBase = true;
199+
for (const auto &Base : CXXRD->bases()) {
200+
if (!FirstBase)
201+
OS << ", ";
202+
if (Base.isVirtual())
203+
OS << "virtual ";
204+
OS << getAccessSpelling(Base.getAccessSpecifier()) << " ";
205+
OS << Base.getType().getAsString(Policy);
206+
FirstBase = false;
207+
}
208+
}
209+
return Result;
210+
}
211+
38212
// A function to extract the appropriate relative path for a given info's
39213
// documentation. The path returned is a composite of the parent namespaces.
40214
//
@@ -408,7 +582,6 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
408582
ASTContext &Context = E->getASTContext();
409583
if (RawComment *Comment =
410584
E->getASTContext().getRawCommentForDeclNoCache(E)) {
411-
CommentInfo CInfo;
412585
Comment->setAttached();
413586
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
414587
EnumValueInfo &Member = I.Members.back();
@@ -434,6 +607,7 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
434607
// Don't parse bases if this isn't a definition.
435608
if (!D->isThisDeclarationADefinition())
436609
return;
610+
437611
for (const CXXBaseSpecifier &B : D->bases()) {
438612
if (B.isVirtual())
439613
continue;
@@ -549,6 +723,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
549723
populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
550724
auto &LO = D->getLangOpts();
551725
I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
726+
I.Prototype = getFunctionPrototype(D);
552727
parseParameters(I, D);
553728

554729
populateTemplateParameters(I.Template, D);
@@ -680,15 +855,19 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
680855
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
681856
emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
682857
bool PublicOnly) {
858+
683859
auto RI = std::make_unique<RecordInfo>();
684860
bool IsInAnonymousNamespace = false;
861+
685862
populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);
686863
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
687864
return {};
688865

689866
RI->TagType = D->getTagKind();
690867
parseFields(*RI, D, PublicOnly);
868+
691869
if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
870+
RI->FullName = getRecordPrototype(C);
692871
if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
693872
RI->Name = TD->getNameAsString();
694873
RI->IsTypeDef = true;
@@ -710,11 +889,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
710889

711890
// What this is a specialization of.
712891
auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
713-
if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
714-
Specialization.SpecializationOf = getUSRForDecl(CTD);
715-
else if (auto *CTPSD =
892+
if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
893+
Specialization.SpecializationOf = getUSRForDecl(SpecTD);
894+
else if (auto *SpecTD =
716895
dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
717-
Specialization.SpecializationOf = getUSRForDecl(CTPSD);
896+
Specialization.SpecializationOf = getUSRForDecl(SpecTD);
718897

719898
// Parameters to the specialization. For partial specializations, get the
720899
// parameters "as written" from the ClassTemplatePartialSpecializationDecl
@@ -786,25 +965,42 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
786965
return {nullptr, makeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
787966
}
788967

968+
static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
969+
assert(D && "Invalid Decl when extracting comment");
970+
ASTContext &Context = D->getASTContext();
971+
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
972+
if (!Comment)
973+
return;
974+
975+
Comment->setAttached();
976+
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
977+
Info.Description.emplace_back();
978+
parseFullComment(Fc, Info.Description.back());
979+
}
980+
}
981+
789982
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
790983
emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
791984
bool PublicOnly) {
792985
TypedefInfo Info;
793986
bool IsInAnonymousNamespace = false;
794987
populateInfo(Info, D, FC, IsInAnonymousNamespace);
988+
795989
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
796990
return {};
797991

798992
Info.DefLoc = Loc;
799993
auto &LO = D->getLangOpts();
800994
Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
995+
801996
if (Info.Underlying.Type.Name.empty()) {
802997
// Typedef for an unnamed type. This is like "typedef struct { } Foo;"
803998
// The record serializer explicitly checks for this syntax and constructs
804999
// a record with that name, so we don't want to emit a duplicate here.
8051000
return {};
8061001
}
8071002
Info.IsUsing = false;
1003+
extractCommentFromDecl(D, Info);
8081004

8091005
// Info is wrapped in its parent scope so is returned in the second position.
8101006
return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
@@ -816,17 +1012,19 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
8161012
emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,
8171013
bool PublicOnly) {
8181014
TypedefInfo Info;
819-
8201015
bool IsInAnonymousNamespace = false;
8211016
populateInfo(Info, D, FC, IsInAnonymousNamespace);
8221017
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
8231018
return {};
8241019

8251020
Info.DefLoc = Loc;
826-
auto &LO = D->getLangOpts();
1021+
const LangOptions &LO = D->getLangOpts();
8271022
Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1023+
Info.TypeDeclaration = getTypeAlias(D);
8281024
Info.IsUsing = true;
8291025

1026+
extractCommentFromDecl(D, Info);
1027+
8301028
// Info is wrapped in its parent scope so is returned in the second position.
8311029
return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
8321030
}

0 commit comments

Comments
 (0)