Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 99ed84d

Browse filesBrowse 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 <peter.chou@mail.utoronto.ca>
1 parent 93ac1bf commit 99ed84d
Copy full SHA for 99ed84d

File tree

2 files changed

+219
-9
lines changed
Filter options

2 files changed

+219
-9
lines changed

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

Copy file name to clipboardExpand all lines: clang-tools-extra/clang-doc/Representation.h
+3Lines changed: 3 additions & 0 deletions
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

Copy file name to clipboardExpand all lines: clang-tools-extra/clang-doc/Serialize.cpp
+216-9Lines changed: 216 additions & 9 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.