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//
@@ -242,9 +420,12 @@ static RecordDecl *getRecordDeclForType(const QualType &T) {
242420static TypeInfo getTypeInfoForType (const QualType &T,
243421 const PrintingPolicy &Policy) {
244422 const TagDecl *TD = getTagDeclForType (T);
245- if (!TD)
246- return TypeInfo (Reference (SymbolID (), T.getAsString (Policy)));
247-
423+ if (!TD) {
424+ TypeInfo TI = TypeInfo (Reference (SymbolID (), T.getAsString (Policy)));
425+ TI.IsBuiltIn = T->isBuiltinType ();
426+ TI.IsTemplate = T->isTemplateTypeParmType ();
427+ return TI;
428+ }
248429 InfoType IT;
249430 if (isa<EnumDecl>(TD)) {
250431 IT = InfoType::IT_enum;
@@ -253,8 +434,12 @@ static TypeInfo getTypeInfoForType(const QualType &T,
253434 } else {
254435 IT = InfoType::IT_default;
255436 }
256- return TypeInfo (Reference (getUSRForDecl (TD), TD->getNameAsString (), IT,
257- T.getAsString (Policy), getInfoRelativePath (TD)));
437+ Reference R = Reference (getUSRForDecl (TD), TD->getNameAsString (), IT,
438+ T.getAsString (Policy), getInfoRelativePath (TD));
439+ TypeInfo TI = TypeInfo (R);
440+ TI.IsBuiltIn = T->isBuiltinType ();
441+ TI.IsTemplate = T->isTemplateTypeParmType ();
442+ return TI;
258443}
259444
260445static bool isPublic (const clang::AccessSpecifier AS,
@@ -408,7 +593,6 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
408593 ASTContext &Context = E->getASTContext ();
409594 if (RawComment *Comment =
410595 E->getASTContext ().getRawCommentForDeclNoCache (E)) {
411- CommentInfo CInfo;
412596 Comment->setAttached ();
413597 if (comments::FullComment *Fc = Comment->parse (Context, nullptr , E)) {
414598 EnumValueInfo &Member = I.Members .back ();
@@ -434,6 +618,7 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
434618 // Don't parse bases if this isn't a definition.
435619 if (!D->isThisDeclarationADefinition ())
436620 return ;
621+
437622 for (const CXXBaseSpecifier &B : D->bases ()) {
438623 if (B.isVirtual ())
439624 continue ;
@@ -555,6 +740,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
555740 populateSymbolInfo (I, D, FC, Loc, IsInAnonymousNamespace);
556741 auto &LO = D->getLangOpts ();
557742 I.ReturnType = getTypeInfoForType (D->getReturnType (), LO);
743+ I.Prototype = getFunctionPrototype (D);
558744 parseParameters (I, D);
559745
560746 populateTemplateParameters (I.Template , D);
@@ -686,15 +872,19 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
686872std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
687873emitInfo (const RecordDecl *D, const FullComment *FC, Location Loc,
688874 bool PublicOnly) {
875+
689876 auto RI = std::make_unique<RecordInfo>();
690877 bool IsInAnonymousNamespace = false ;
878+
691879 populateSymbolInfo (*RI, D, FC, Loc, IsInAnonymousNamespace);
692880 if (!shouldSerializeInfo (PublicOnly, IsInAnonymousNamespace, D))
693881 return {};
694882
695883 RI->TagType = D->getTagKind ();
696884 parseFields (*RI, D, PublicOnly);
885+
697886 if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
887+ RI->FullName = getRecordPrototype (C);
698888 if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl ()) {
699889 RI->Name = TD->getNameAsString ();
700890 RI->IsTypeDef = true ;
@@ -716,11 +906,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
716906
717907 // What this is a specialization of.
718908 auto SpecOf = CTSD->getSpecializedTemplateOrPartial ();
719- if (auto *CTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
720- Specialization.SpecializationOf = getUSRForDecl (CTD );
721- else if (auto *CTPSD =
909+ if (auto *SpecTD = dyn_cast<ClassTemplateDecl *>(SpecOf))
910+ Specialization.SpecializationOf = getUSRForDecl (SpecTD );
911+ else if (auto *SpecTD =
722912 dyn_cast<ClassTemplatePartialSpecializationDecl *>(SpecOf))
723- Specialization.SpecializationOf = getUSRForDecl (CTPSD );
913+ Specialization.SpecializationOf = getUSRForDecl (SpecTD );
724914
725915 // Parameters to the specialization. For partial specializations, get the
726916 // parameters "as written" from the ClassTemplatePartialSpecializationDecl
@@ -792,25 +982,42 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
792982 return {nullptr , makeAndInsertIntoParent<FunctionInfo &&>(std::move (Func))};
793983}
794984
985+ static void extractCommentFromDecl (const Decl *D, TypedefInfo &Info) {
986+ assert (D && " Invalid Decl when extracting comment" );
987+ ASTContext &Context = D->getASTContext ();
988+ RawComment *Comment = Context.getRawCommentForDeclNoCache (D);
989+ if (!Comment)
990+ return ;
991+
992+ Comment->setAttached ();
993+ if (comments::FullComment *Fc = Comment->parse (Context, nullptr , D)) {
994+ Info.Description .emplace_back ();
995+ parseFullComment (Fc, Info.Description .back ());
996+ }
997+ }
998+
795999std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
7961000emitInfo (const TypedefDecl *D, const FullComment *FC, Location Loc,
7971001 bool PublicOnly) {
7981002 TypedefInfo Info;
7991003 bool IsInAnonymousNamespace = false ;
8001004 populateInfo (Info, D, FC, IsInAnonymousNamespace);
1005+
8011006 if (!shouldSerializeInfo (PublicOnly, IsInAnonymousNamespace, D))
8021007 return {};
8031008
8041009 Info.DefLoc = Loc;
8051010 auto &LO = D->getLangOpts ();
8061011 Info.Underlying = getTypeInfoForType (D->getUnderlyingType (), LO);
1012+
8071013 if (Info.Underlying .Type .Name .empty ()) {
8081014 // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
8091015 // The record serializer explicitly checks for this syntax and constructs
8101016 // a record with that name, so we don't want to emit a duplicate here.
8111017 return {};
8121018 }
8131019 Info.IsUsing = false ;
1020+ extractCommentFromDecl (D, Info);
8141021
8151022 // Info is wrapped in its parent scope so is returned in the second position.
8161023 return {nullptr , makeAndInsertIntoParent<TypedefInfo &&>(std::move (Info))};
@@ -822,17 +1029,19 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
8221029emitInfo (const TypeAliasDecl *D, const FullComment *FC, Location Loc,
8231030 bool PublicOnly) {
8241031 TypedefInfo Info;
825-
8261032 bool IsInAnonymousNamespace = false ;
8271033 populateInfo (Info, D, FC, IsInAnonymousNamespace);
8281034 if (!shouldSerializeInfo (PublicOnly, IsInAnonymousNamespace, D))
8291035 return {};
8301036
8311037 Info.DefLoc = Loc;
832- auto &LO = D->getLangOpts ();
1038+ const LangOptions &LO = D->getLangOpts ();
8331039 Info.Underlying = getTypeInfoForType (D->getUnderlyingType (), LO);
1040+ Info.TypeDeclaration = getTypeAlias (D);
8341041 Info.IsUsing = true ;
8351042
1043+ extractCommentFromDecl (D, Info);
1044+
8361045 // Info is wrapped in its parent scope so is returned in the second position.
8371046 return {nullptr , makeAndInsertIntoParent<TypedefInfo &&>(std::move (Info))};
8381047}
0 commit comments