1 //===-- Serialize.cpp - ClangDoc Serializer ---------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Serialize.h"
10 #include "BitcodeWriter.h"
11 #include "clang/AST/Comment.h"
12 #include "clang/Index/USRGeneration.h"
13 #include "llvm/ADT/Hashing.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/Support/SHA1.h"
16 
17 using clang::comments::FullComment;
18 
19 namespace clang {
20 namespace doc {
21 namespace serialize {
22 
23 SymbolID hashUSR(llvm::StringRef USR) {
24   return llvm::SHA1::hash(arrayRefFromStringRef(USR));
25 }
26 
27 class ClangDocCommentVisitor
28     : public ConstCommentVisitor<ClangDocCommentVisitor> {
29 public:
30   ClangDocCommentVisitor(CommentInfo &CI) : CurrentCI(CI) {}
31 
32   void parseComment(const comments::Comment *C);
33 
34   void visitTextComment(const TextComment *C);
35   void visitInlineCommandComment(const InlineCommandComment *C);
36   void visitHTMLStartTagComment(const HTMLStartTagComment *C);
37   void visitHTMLEndTagComment(const HTMLEndTagComment *C);
38   void visitBlockCommandComment(const BlockCommandComment *C);
39   void visitParamCommandComment(const ParamCommandComment *C);
40   void visitTParamCommandComment(const TParamCommandComment *C);
41   void visitVerbatimBlockComment(const VerbatimBlockComment *C);
42   void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C);
43   void visitVerbatimLineComment(const VerbatimLineComment *C);
44 
45 private:
46   std::string getCommandName(unsigned CommandID) const;
47   bool isWhitespaceOnly(StringRef S) const;
48 
49   CommentInfo &CurrentCI;
50 };
51 
52 void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
53   CurrentCI.Kind = C->getCommentKindName();
54   ConstCommentVisitor<ClangDocCommentVisitor>::visit(C);
55   for (comments::Comment *Child :
56        llvm::make_range(C->child_begin(), C->child_end())) {
57     CurrentCI.Children.emplace_back(llvm::make_unique<CommentInfo>());
58     ClangDocCommentVisitor Visitor(*CurrentCI.Children.back());
59     Visitor.parseComment(Child);
60   }
61 }
62 
63 void ClangDocCommentVisitor::visitTextComment(const TextComment *C) {
64   if (!isWhitespaceOnly(C->getText()))
65     CurrentCI.Text = C->getText();
66 }
67 
68 void ClangDocCommentVisitor::visitInlineCommandComment(
69     const InlineCommandComment *C) {
70   CurrentCI.Name = getCommandName(C->getCommandID());
71   for (unsigned I = 0, E = C->getNumArgs(); I != E; ++I)
72     CurrentCI.Args.push_back(C->getArgText(I));
73 }
74 
75 void ClangDocCommentVisitor::visitHTMLStartTagComment(
76     const HTMLStartTagComment *C) {
77   CurrentCI.Name = C->getTagName();
78   CurrentCI.SelfClosing = C->isSelfClosing();
79   for (unsigned I = 0, E = C->getNumAttrs(); I < E; ++I) {
80     const HTMLStartTagComment::Attribute &Attr = C->getAttr(I);
81     CurrentCI.AttrKeys.push_back(Attr.Name);
82     CurrentCI.AttrValues.push_back(Attr.Value);
83   }
84 }
85 
86 void ClangDocCommentVisitor::visitHTMLEndTagComment(
87     const HTMLEndTagComment *C) {
88   CurrentCI.Name = C->getTagName();
89   CurrentCI.SelfClosing = true;
90 }
91 
92 void ClangDocCommentVisitor::visitBlockCommandComment(
93     const BlockCommandComment *C) {
94   CurrentCI.Name = getCommandName(C->getCommandID());
95   for (unsigned I = 0, E = C->getNumArgs(); I < E; ++I)
96     CurrentCI.Args.push_back(C->getArgText(I));
97 }
98 
99 void ClangDocCommentVisitor::visitParamCommandComment(
100     const ParamCommandComment *C) {
101   CurrentCI.Direction =
102       ParamCommandComment::getDirectionAsString(C->getDirection());
103   CurrentCI.Explicit = C->isDirectionExplicit();
104   if (C->hasParamName())
105     CurrentCI.ParamName = C->getParamNameAsWritten();
106 }
107 
108 void ClangDocCommentVisitor::visitTParamCommandComment(
109     const TParamCommandComment *C) {
110   if (C->hasParamName())
111     CurrentCI.ParamName = C->getParamNameAsWritten();
112 }
113 
114 void ClangDocCommentVisitor::visitVerbatimBlockComment(
115     const VerbatimBlockComment *C) {
116   CurrentCI.Name = getCommandName(C->getCommandID());
117   CurrentCI.CloseName = C->getCloseName();
118 }
119 
120 void ClangDocCommentVisitor::visitVerbatimBlockLineComment(
121     const VerbatimBlockLineComment *C) {
122   if (!isWhitespaceOnly(C->getText()))
123     CurrentCI.Text = C->getText();
124 }
125 
126 void ClangDocCommentVisitor::visitVerbatimLineComment(
127     const VerbatimLineComment *C) {
128   if (!isWhitespaceOnly(C->getText()))
129     CurrentCI.Text = C->getText();
130 }
131 
132 bool ClangDocCommentVisitor::isWhitespaceOnly(llvm::StringRef S) const {
133   return std::all_of(S.begin(), S.end(), isspace);
134 }
135 
136 std::string ClangDocCommentVisitor::getCommandName(unsigned CommandID) const {
137   const CommandInfo *Info = CommandTraits::getBuiltinCommandInfo(CommandID);
138   if (Info)
139     return Info->Name;
140   // TODO: Add parsing for \file command.
141   return "<not a builtin command>";
142 }
143 
144 // Serializing functions.
145 
146 template <typename T> static std::string serialize(T &I) {
147   SmallString<2048> Buffer;
148   llvm::BitstreamWriter Stream(Buffer);
149   ClangDocBitcodeWriter Writer(Stream);
150   Writer.emitBlock(I);
151   return Buffer.str().str();
152 }
153 
154 std::string serialize(std::unique_ptr<Info> &I) {
155   switch (I->IT) {
156   case InfoType::IT_namespace:
157     return serialize(*static_cast<NamespaceInfo *>(I.get()));
158   case InfoType::IT_record:
159     return serialize(*static_cast<RecordInfo *>(I.get()));
160   case InfoType::IT_enum:
161     return serialize(*static_cast<EnumInfo *>(I.get()));
162   case InfoType::IT_function:
163     return serialize(*static_cast<FunctionInfo *>(I.get()));
164   default:
165     return "";
166   }
167 }
168 
169 static void parseFullComment(const FullComment *C, CommentInfo &CI) {
170   ClangDocCommentVisitor Visitor(CI);
171   Visitor.parseComment(C);
172 }
173 
174 static SymbolID getUSRForDecl(const Decl *D) {
175   llvm::SmallString<128> USR;
176   if (index::generateUSRForDecl(D, USR))
177     return SymbolID();
178   return hashUSR(USR);
179 }
180 
181 static RecordDecl *getDeclForType(const QualType &T) {
182   auto *Ty = T->getAs<RecordType>();
183   if (!Ty)
184     return nullptr;
185   return Ty->getDecl()->getDefinition();
186 }
187 
188 static bool isPublic(const clang::AccessSpecifier AS,
189                      const clang::Linkage Link) {
190   if (AS == clang::AccessSpecifier::AS_private)
191     return false;
192   else if ((Link == clang::Linkage::ModuleLinkage) ||
193            (Link == clang::Linkage::ExternalLinkage))
194     return true;
195   return false; // otherwise, linkage is some form of internal linkage
196 }
197 
198 static void parseFields(RecordInfo &I, const RecordDecl *D, bool PublicOnly) {
199   for (const FieldDecl *F : D->fields()) {
200     if (PublicOnly && !isPublic(F->getAccessUnsafe(), F->getLinkageInternal()))
201       continue;
202     if (const auto *T = getDeclForType(F->getTypeSourceInfo()->getType())) {
203       // Use getAccessUnsafe so that we just get the default AS_none if it's not
204       // valid, as opposed to an assert.
205       if (const auto *N = dyn_cast<EnumDecl>(T)) {
206         I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
207                                InfoType::IT_enum, F->getNameAsString(),
208                                N->getAccessUnsafe());
209         continue;
210       } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
211         I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
212                                InfoType::IT_record, F->getNameAsString(),
213                                N->getAccessUnsafe());
214         continue;
215       }
216     }
217     I.Members.emplace_back(F->getTypeSourceInfo()->getType().getAsString(),
218                            F->getNameAsString(), F->getAccessUnsafe());
219   }
220 }
221 
222 static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
223   for (const EnumConstantDecl *E : D->enumerators())
224     I.Members.emplace_back(E->getNameAsString());
225 }
226 
227 static void parseParameters(FunctionInfo &I, const FunctionDecl *D) {
228   for (const ParmVarDecl *P : D->parameters()) {
229     if (const auto *T = getDeclForType(P->getOriginalType())) {
230       if (const auto *N = dyn_cast<EnumDecl>(T)) {
231         I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
232                               InfoType::IT_enum, P->getNameAsString());
233         continue;
234       } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
235         I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
236                               InfoType::IT_record, P->getNameAsString());
237         continue;
238       }
239     }
240     I.Params.emplace_back(P->getOriginalType().getAsString(),
241                           P->getNameAsString());
242   }
243 }
244 
245 static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
246   // Don't parse bases if this isn't a definition.
247   if (!D->isThisDeclarationADefinition())
248     return;
249   for (const CXXBaseSpecifier &B : D->bases()) {
250     if (B.isVirtual())
251       continue;
252     if (const auto *P = getDeclForType(B.getType()))
253       I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
254                              InfoType::IT_record);
255     else
256       I.Parents.emplace_back(B.getType().getAsString());
257   }
258   for (const CXXBaseSpecifier &B : D->vbases()) {
259     if (const auto *P = getDeclForType(B.getType()))
260       I.VirtualParents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
261                                     InfoType::IT_record);
262     else
263       I.VirtualParents.emplace_back(B.getType().getAsString());
264   }
265 }
266 
267 template <typename T>
268 static void
269 populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
270                          const T *D) {
271   const auto *DC = dyn_cast<DeclContext>(D);
272   while ((DC = DC->getParent())) {
273     if (const auto *N = dyn_cast<NamespaceDecl>(DC))
274       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
275                               InfoType::IT_namespace);
276     else if (const auto *N = dyn_cast<RecordDecl>(DC))
277       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
278                               InfoType::IT_record);
279     else if (const auto *N = dyn_cast<FunctionDecl>(DC))
280       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
281                               InfoType::IT_function);
282     else if (const auto *N = dyn_cast<EnumDecl>(DC))
283       Namespaces.emplace_back(getUSRForDecl(N), N->getNameAsString(),
284                               InfoType::IT_enum);
285   }
286 }
287 
288 template <typename T>
289 static void populateInfo(Info &I, const T *D, const FullComment *C) {
290   I.USR = getUSRForDecl(D);
291   I.Name = D->getNameAsString();
292   populateParentNamespaces(I.Namespace, D);
293   if (C) {
294     I.Description.emplace_back();
295     parseFullComment(C, I.Description.back());
296   }
297 }
298 
299 template <typename T>
300 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
301                                int LineNumber, StringRef Filename) {
302   populateInfo(I, D, C);
303   if (D->isThisDeclarationADefinition())
304     I.DefLoc.emplace(LineNumber, Filename);
305   else
306     I.Loc.emplace_back(LineNumber, Filename);
307 }
308 
309 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
310                                  const FullComment *FC, int LineNumber,
311                                  StringRef Filename) {
312   populateSymbolInfo(I, D, FC, LineNumber, Filename);
313   if (const auto *T = getDeclForType(D->getReturnType())) {
314     if (dyn_cast<EnumDecl>(T))
315       I.ReturnType =
316           TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_enum);
317     else if (dyn_cast<RecordDecl>(T))
318       I.ReturnType =
319           TypeInfo(getUSRForDecl(T), T->getNameAsString(), InfoType::IT_record);
320   } else {
321     I.ReturnType = TypeInfo(D->getReturnType().getAsString());
322   }
323   parseParameters(I, D);
324 }
325 
326 std::unique_ptr<Info> emitInfo(const NamespaceDecl *D, const FullComment *FC,
327                                int LineNumber, llvm::StringRef File,
328                                bool PublicOnly) {
329   if (PublicOnly && ((D->isAnonymousNamespace()) ||
330                      !isPublic(D->getAccess(), D->getLinkageInternal())))
331     return nullptr;
332   auto I = llvm::make_unique<NamespaceInfo>();
333   populateInfo(*I, D, FC);
334   return std::unique_ptr<Info>{std::move(I)};
335 }
336 
337 std::unique_ptr<Info> emitInfo(const RecordDecl *D, const FullComment *FC,
338                                int LineNumber, llvm::StringRef File,
339                                bool PublicOnly) {
340   if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
341     return nullptr;
342   auto I = llvm::make_unique<RecordInfo>();
343   populateSymbolInfo(*I, D, FC, LineNumber, File);
344   I->TagType = D->getTagKind();
345   parseFields(*I, D, PublicOnly);
346   if (const auto *C = dyn_cast<CXXRecordDecl>(D))
347     parseBases(*I, C);
348   return std::unique_ptr<Info>{std::move(I)};
349 }
350 
351 std::unique_ptr<Info> emitInfo(const FunctionDecl *D, const FullComment *FC,
352                                int LineNumber, llvm::StringRef File,
353                                bool PublicOnly) {
354   if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
355     return nullptr;
356   FunctionInfo Func;
357   populateFunctionInfo(Func, D, FC, LineNumber, File);
358   Func.Access = clang::AccessSpecifier::AS_none;
359 
360   // Wrap in enclosing scope
361   auto I = llvm::make_unique<NamespaceInfo>();
362   if (!Func.Namespace.empty())
363     I->USR = Func.Namespace[0].USR;
364   else
365     I->USR = SymbolID();
366   I->ChildFunctions.emplace_back(std::move(Func));
367   return std::unique_ptr<Info>{std::move(I)};
368 }
369 
370 std::unique_ptr<Info> emitInfo(const CXXMethodDecl *D, const FullComment *FC,
371                                int LineNumber, llvm::StringRef File,
372                                bool PublicOnly) {
373   if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
374     return nullptr;
375   FunctionInfo Func;
376   populateFunctionInfo(Func, D, FC, LineNumber, File);
377   Func.IsMethod = true;
378 
379   SymbolID ParentUSR = getUSRForDecl(D->getParent());
380   Func.Parent = Reference{ParentUSR, D->getParent()->getNameAsString(),
381                           InfoType::IT_record};
382   Func.Access = D->getAccess();
383 
384   // Wrap in enclosing scope
385   auto I = llvm::make_unique<RecordInfo>();
386   I->USR = ParentUSR;
387   I->ChildFunctions.emplace_back(std::move(Func));
388   return std::unique_ptr<Info>{std::move(I)};
389 }
390 
391 std::unique_ptr<Info> emitInfo(const EnumDecl *D, const FullComment *FC,
392                                int LineNumber, llvm::StringRef File,
393                                bool PublicOnly) {
394   if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal()))
395     return nullptr;
396   EnumInfo Enum;
397   populateSymbolInfo(Enum, D, FC, LineNumber, File);
398   Enum.Scoped = D->isScoped();
399   parseEnumerators(Enum, D);
400 
401   // Wrap in enclosing scope
402   if (!Enum.Namespace.empty()) {
403     switch (Enum.Namespace[0].RefType) {
404     case InfoType::IT_namespace: {
405       auto I = llvm::make_unique<NamespaceInfo>();
406       I->USR = Enum.Namespace[0].USR;
407       I->ChildEnums.emplace_back(std::move(Enum));
408       return std::unique_ptr<Info>{std::move(I)};
409     }
410     case InfoType::IT_record: {
411       auto I = llvm::make_unique<RecordInfo>();
412       I->USR = Enum.Namespace[0].USR;
413       I->ChildEnums.emplace_back(std::move(Enum));
414       return std::unique_ptr<Info>{std::move(I)};
415     }
416     default:
417       break;
418     }
419   }
420 
421   // Put in global namespace
422   auto I = llvm::make_unique<NamespaceInfo>();
423   I->USR = SymbolID();
424   I->ChildEnums.emplace_back(std::move(Enum));
425   return std::unique_ptr<Info>{std::move(I)};
426 }
427 
428 } // namespace serialize
429 } // namespace doc
430 } // namespace clang
431