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,
680855std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
681856emitInfo (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+
789982std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
790983emitInfo (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>>
8161012emitInfo (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