Skip to content

Commit f67bcd6

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 5c5d241 commit f67bcd6

File tree

2 files changed

+219
-9
lines changed

2 files changed

+219
-9
lines changed

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

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

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

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

+216-9
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,188 @@ static void populateMemberTypeInfo(RecordInfo &I, AccessSpecifier &Access,
3535
const DeclaratorDecl *D,
3636
bool IsStatic = false);
3737

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

554737
populateTemplateParameters(I.Template, D);
@@ -680,15 +863,19 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
680863
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
681864
emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
682865
bool PublicOnly) {
866+
683867
auto RI = std::make_unique<RecordInfo>();
684868
bool IsInAnonymousNamespace = false;
869+
685870
populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);
686871
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
687872
return {};
688873

689874
RI->TagType = D->getTagKind();
690875
parseFields(*RI, D, PublicOnly);
876+
691877
if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
878+
RI->FullName = getRecordPrototype(C);
692879
if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
693880
RI->Name = TD->getNameAsString();
694881
RI->IsTypeDef = true;
@@ -710,11 +897,12 @@ emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
710897

711898
// What this is a specialization of.
712899
auto SpecOf = CTSD->getSpecializedTemplateOrPartial();
713-
if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
714-
Specialization.SpecializationOf = getUSRForDecl(CTD);
715-
else if (auto *CTPSD =
716-
dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
717-
Specialization.SpecializationOf = getUSRForDecl(CTPSD);
900+
if (auto *SpecPtr = dyn_cast<ClassTemplateDecl *>(SpecOf)) {
901+
Specialization.SpecializationOf = getUSRForDecl(SpecPtr);
902+
} else if (auto *SpecPtr =
903+
dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf)) {
904+
Specialization.SpecializationOf = getUSRForDecl(SpecPtr);
905+
}
718906

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

977+
static void extractCommentFromDecl(const Decl *D, TypedefInfo &Info) {
978+
assert(D && "Invalid Decl when extracting comment");
979+
ASTContext &Context = D->getASTContext();
980+
RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
981+
if (!Comment)
982+
return;
983+
984+
Comment->setAttached();
985+
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
986+
Info.Description.emplace_back();
987+
parseFullComment(Fc, Info.Description.back());
988+
}
989+
}
990+
789991
std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
790992
emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
791993
bool PublicOnly) {
792994
TypedefInfo Info;
793995
bool IsInAnonymousNamespace = false;
794996
populateInfo(Info, D, FC, IsInAnonymousNamespace);
997+
795998
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
796999
return {};
7971000

7981001
Info.DefLoc = Loc;
7991002
auto &LO = D->getLangOpts();
8001003
Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1004+
8011005
if (Info.Underlying.Type.Name.empty()) {
8021006
// Typedef for an unnamed type. This is like "typedef struct { } Foo;"
8031007
// The record serializer explicitly checks for this syntax and constructs
8041008
// a record with that name, so we don't want to emit a duplicate here.
8051009
return {};
8061010
}
8071011
Info.IsUsing = false;
1012+
extractCommentFromDecl(D, Info);
8081013

8091014
// Info is wrapped in its parent scope so is returned in the second position.
8101015
return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
@@ -816,17 +1021,19 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
8161021
emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,
8171022
bool PublicOnly) {
8181023
TypedefInfo Info;
819-
8201024
bool IsInAnonymousNamespace = false;
8211025
populateInfo(Info, D, FC, IsInAnonymousNamespace);
8221026
if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
8231027
return {};
8241028

8251029
Info.DefLoc = Loc;
826-
auto &LO = D->getLangOpts();
1030+
const LangOptions &LO = D->getLangOpts();
8271031
Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
1032+
Info.TypeDeclaration = getTypeAlias(D);
8281033
Info.IsUsing = true;
8291034

1035+
extractCommentFromDecl(D, Info);
1036+
8301037
// Info is wrapped in its parent scope so is returned in the second position.
8311038
return {nullptr, makeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
8321039
}

0 commit comments

Comments
 (0)