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,184 @@ 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+
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+ Stream, TTP->getASTContext ().getPrintingPolicy ());
63+ }
64+ } else if (const auto *NTTP =
65+ llvm::dyn_cast<NonTypeTemplateParmDecl>(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>(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>(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 (Stream, 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);
108+ QualType ParamType = ParamDecl->getType ();
109+ ParamType.print (Stream, 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+ const Expr *DefaultArg = ParamDecl->getDefaultArg ();
118+ if (DefaultArg) {
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 (Stream, 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 (Base.getAccessSpecifier ()) << " " ;
209+ OS << Base.getType ().getAsString (Policy);
210+ FirstBase = false ;
211+ }
212+ }
213+ return Result;
214+ }
215+
38216// A function to extract the appropriate relative path for a given info's
39217// documentation. The path returned is a composite of the parent namespaces.
40218//
@@ -408,7 +586,6 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
408586 ASTContext &Context = E->getASTContext ();
409587 if (RawComment *Comment =
410588 E->getASTContext ().getRawCommentForDeclNoCache (E)) {
411- CommentInfo CInfo;
412589 Comment->setAttached ();
413590 if (comments::FullComment *Fc = Comment->parse (Context, nullptr , E)) {
414591 EnumValueInfo &Member = I.Members .back ();
@@ -434,6 +611,7 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
434611 // Don't parse bases if this isn't a definition.
435612 if (!D->isThisDeclarationADefinition ())
436613 return ;
614+
437615 for (const CXXBaseSpecifier &B : D->bases ()) {
438616 if (B.isVirtual ())
439617 continue ;
@@ -555,6 +733,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
555733 populateSymbolInfo (I, D, FC, Loc, IsInAnonymousNamespace);
556734 auto &LO = D->getLangOpts ();
557735 I.ReturnType = getTypeInfoForType (D->getReturnType (), LO);
736+ I.Prototype = getFunctionPrototype (D);
558737 parseParameters (I, D);
559738
560739 populateTemplateParameters (I.Template , D);
@@ -686,15 +865,19 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
686865std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
687866emitInfo (const RecordDecl *D, const FullComment *FC, Location Loc,
688867 bool PublicOnly) {
868+
689869 auto RI = std::make_unique<RecordInfo>();
690870 bool IsInAnonymousNamespace = false ;
871+
691872 populateSymbolInfo (*RI, D, FC, Loc, IsInAnonymousNamespace);
692873 if (!shouldSerializeInfo (PublicOnly, IsInAnonymousNamespace, D))
693874 return {};
694875
695876 RI->TagType = D->getTagKind ();
696877 parseFields (*RI, D, PublicOnly);
878+
697879 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
880+ RI->FullName = getRecordPrototype (C);
698881 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl ()) {
699882 RI->Name = TD->getNameAsString ();
700883 RI->IsTypeDef = true ;
@@ -716,11 +899,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
716899
717900 // What this is a specialization of.
718901 auto SpecOf = CTSD->getSpecializedTemplateOrPartial ();
719- if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
720- Specialization.SpecializationOf = getUSRForDecl (CTD );
721- else if (auto *CTPSD =
902+ if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
903+ Specialization.SpecializationOf = getUSRForDecl (SpecTD );
904+ else if (auto *SpecTD =
722905 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
723- Specialization.SpecializationOf = getUSRForDecl (CTPSD );
906+ Specialization.SpecializationOf = getUSRForDecl (SpecTD );
724907
725908 // Parameters to the specialization. For partial specializations, get the
726909 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
@@ -792,25 +975,42 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
792975 return {nullptr , makeAndInsertIntoParent<FunctionInfo &&>(std::move (Func))};
793976}
794977
978+ static void extractCommentFromDecl (const Decl *D, TypedefInfo &Info) {
979+ assert (D && " Invalid Decl when extracting comment" );
980+ ASTContext &Context = D->getASTContext ();
981+ RawComment *Comment = Context.getRawCommentForDeclNoCache (D);
982+ if (!Comment)
983+ return ;
984+
985+ Comment->setAttached ();
986+ if (comments::FullComment *Fc = Comment->parse (Context, nullptr , D)) {
987+ Info.Description .emplace_back ();
988+ parseFullComment (Fc, Info.Description .back ());
989+ }
990+ }
991+
795992std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
796993emitInfo (const TypedefDecl *D, const FullComment *FC, Location Loc,
797994 bool PublicOnly) {
798995 TypedefInfo Info;
799996 bool IsInAnonymousNamespace = false ;
800997 populateInfo (Info, D, FC, IsInAnonymousNamespace);
998+
801999 if (!shouldSerializeInfo (PublicOnly, IsInAnonymousNamespace, D))
8021000 return {};
8031001
8041002 Info.DefLoc = Loc;
8051003 auto &LO = D->getLangOpts ();
8061004 Info.Underlying = getTypeInfoForType (D->getUnderlyingType (), LO);
1005+
8071006 if (Info.Underlying .Type .Name .empty ()) {
8081007 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
8091008 // The record serializer explicitly checks for this syntax and constructs
8101009 // a record with that name, so we don't want to emit a duplicate here.
8111010 return {};
8121011 }
8131012 Info.IsUsing = false ;
1013+ extractCommentFromDecl (D, Info);
8141014
8151015 // Info is wrapped in its parent scope so is returned in the second position.
8161016 return {nullptr , makeAndInsertIntoParent<TypedefInfo &&>(std::move (Info))};
@@ -822,17 +1022,19 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
8221022emitInfo (const TypeAliasDecl *D, const FullComment *FC, Location Loc,
8231023 bool PublicOnly) {
8241024 TypedefInfo Info;
825-
8261025 bool IsInAnonymousNamespace = false ;
8271026 populateInfo (Info, D, FC, IsInAnonymousNamespace);
8281027 if (!shouldSerializeInfo (PublicOnly, IsInAnonymousNamespace, D))
8291028 return {};
8301029
8311030 Info.DefLoc = Loc;
832- auto &LO = D->getLangOpts ();
1031+ const LangOptions &LO = D->getLangOpts ();
8331032 Info.Underlying = getTypeInfoForType (D->getUnderlyingType (), LO);
1033+ Info.TypeDeclaration = getTypeAlias (D);
8341034 Info.IsUsing = true ;
8351035
1036+ extractCommentFromDecl (D, Info);
1037+
8361038 // Info is wrapped in its parent scope so is returned in the second position.
8371039 return {nullptr , makeAndInsertIntoParent<TypedefInfo &&>(std::move (Info))};
8381040}
0 commit comments