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